Go基础语法

102 阅读9分钟

一、Go基础语法

语言结构

main.go

package main
import (
	"fmt"
)
func main() {
	fmt.Println("hello world")
}

第一行,表示该文件属于main包。在 Go 语言中,main包是一个特殊的包,它包含了程序的入口点。

第三行导入了一个名为fmt的包。fmt包提供了格式化输入输出的功能,它包含了向控制台输出内容的函数。

main函数中,使用了fmt.Println函数来将字符串"hello world"输出到控制台。Println函数会在输出后自动换行。

运行程序 go run main.go

编译程序 go build main.go

变量

声明变量的两种方式:

  • var identifier type var b, c int = 1, 2
  • := f := float32(e)
package main

import (
	"fmt"
	"math"
)

func main() {

	var a = "initial"

	var b, c int = 1, 2

	var d = true

	var e float64

	f := float32(e)

	g := a + "foo"
	fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
	fmt.Println(g)                // initialapple

	const s string = "constant"
	const h = 500000000
	const i = 3e20 / h
	fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}

if else

if 后面必须接大括号

if num := 9; num < 0 {
	fmt.Println(num, "is negative")
} else if num < 10 {
	fmt.Println(num, "has 1 digit")
} else {
	fmt.Println(num, "has multiple digits")
}

循环

在go里面设有while循环、do while循环,只有唯一的一种for循环

for n := 0; n < 5; n++ {
    if n%2 == 0 {
        continue
    }
    fmt.Println(n)
}

switch

在c++里面,switch case如果不显示加break的话会然后会继续往下跑完所有的case,

在go语言里面的话不需要加break

package main

import (
	"fmt"
	"time"
)

func main() {

	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}
/*
two
It's after noon
*/

数组

Go 语言数组声明需要指定元素类型及元素个数;

var variable_name [SIZE] variable_type

func main() {

	var a [5]int
	a[4] = 100
	fmt.Println("get:", a[2])
	fmt.Println("len:", len(a))

	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println(b)

	var twoD [2][3]int
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d: ", twoD)
}
/*
get: 0
len: 5
[1 2 3 4 5]
2d:  [[0 1 2] [1 2 3]]
*/

切片

与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大;

make创建切片;append追加元素;

声明一个未指定大小的数组来定义切片var identifier []type

使用make var slice1 []type = make([]type, len)

package main
import "fmt"
func main() {
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[2])   // c
	fmt.Println("len:", len(s)) // 3

	s = append(s, "d")
	s = append(s, "e", "f")
	fmt.Println(s) // [a b c d e f]

	c := make([]string, len(s))
	copy(c, s)
	fmt.Println(c) // [a b c d e f]

	fmt.Println(s[2:5]) // [c d e]
	fmt.Println(s[:5])  // [a b c d e]
	fmt.Println(s[2:])  // [c d e f]

	good := []string{"g", "o", "o", "d"}
	fmt.Println(good) // [g o o d]
}

map

golang中的map是完全无序

package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m)           // map[one:1 two:2]
	fmt.Println(len(m))      // 2
	fmt.Println(m["one"])    // 1
	fmt.Println(m["unknow"]) // 0

	r, ok := m["unknow"]
	fmt.Println(r, ok) // 0 false

	delete(m, "one")

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}

range

用于快速遍历slice;map;

返回第一个值为索引,第二个为对应位置值,不需要索引可以用下划线忽略

func main() {
	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += num
		if num == 2 {
			fmt.Println("index:", i, "num:", num) // index: 0 num: 2
		}
	}
	fmt.Println(sum) // 9

	m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v) // b 8; a A
	}
	for k := range m {
		fmt.Println("key", k) // key a; key b
	}
}

函数

  • 变量类型后置
  • 支持返回多个值
package main

import "fmt"

func add(a int, b int) int {
	return a + b
}

func add2(a, b int) int {
	return a + b
}

func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

func main() {
	res := add(1, 2)
	fmt.Println(res) // 3

	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
}

指针

go里面也支持指针。相比C和C++里面的指针,支持的操作很有限。

指针的一个主要用途就是对于传入参数进行修改。

package main

import "fmt"

func add2(n int) {
	n += 2
}

func add2ptr(n *int) {
	*n += 2
}

func main() {
	n := 5
	add2(n)
	fmt.Println(n) // 5
	add2ptr(&n)
	fmt.Println(n) // 7
}

结构体

  • 可以用结构体名称初始化结构体变量
  • 用键值对指定初始值
package main

import "fmt"

type user struct {
	name     string
	password string
}

func main() {
	a := user{name: "wang", password: "1024"}
	b := user{"wang", "1024"}
	c := user{name: "wang"}
	c.password = "1024"
	var d user
	d.name = "wang"
	d.password = "1024"

	fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
	fmt.Println(checkPassword(a, "haha"))   // false
	fmt.Println(checkPassword2(&a, "haha")) // false
}

func checkPassword(u user, password string) bool {
	return u.password == password
}

func checkPassword2(u *user, password string) bool {
	return u.password == password
}

结构体方法

  • 将结构体类型参数写在函数前
  • 不带指针操作的为一个拷贝,无法对结构体进行修改
package main

import "fmt"

type user struct {
	name     string
	password string
}

func (u user) checkPassword(password string) bool {
	return u.password == password
}
//实现带指针的结构体方法
func (u *user) resetPassword(password string) {
	u.password = password
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.resetPassword("2048")
	fmt.Println(a.checkPassword("2048")) // true
}

错误处理

在函数里面,我们可以在那个函数的返回值类型里面,后面加一个error,就代表这个函数可能会返回错误 那么在函数实现的时候,return需要同时return两个值,要么就是如果出现错误的话,那么可以return nil和一个error。如果没有的话,那么返回原本的结果和nil

package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

func main() {
	u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) // wang

	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err) // not found
		return
	} else {
		fmt.Println(u.name)
	}
}

字符串

基础操作

依次使用了 strings 包中的以下函数和方法:

  • Contains:判断字符串 a 是否包含子串 "ll",结果为 true。
  • Count:统计字符串 a 中出现子串 "l" 的次数,结果为 2。
  • HasPrefix:判断字符串 a 是否以 "he" 开头,结果为 true。
  • HasSuffix:判断字符串 a 是否以 "llo" 结尾,结果为 true。
  • Index:查找子串 "ll" 在字符串 a 中第一次出现的位置,结果为 2。
  • Join:将字符串切片 {"he", "llo"} 用 "-" 连接起来,结果为 "he-llo"。
  • Repeat:将字符串 a 重复两次,结果为 "hellohello"。
  • Replace:将字符串 a 中的 "e" 替换为 "E",-1 表示替换所有匹配项,结果为 "hEllo"。
  • Split:将字符串 "a-b-c" 按照 "-" 分割成字符串切片,结果为 ["a" "b" "c"]。
  • ToLower:将字符串 a 转换为小写字母形式,结果为 "hello"。
  • ToUpper:将字符串 a 转换为大写字母形式,结果为 "HELLO"。
  • len:求字符串 a 和 b 的长度分别为 5 和 6。
package main

import (
	"fmt"
	"strings"
)

func main() {
	a := "hello"
	fmt.Println(strings.Contains(a, "ll"))                // true
	fmt.Println(strings.Count(a, "l"))                    // 2
    // HasPrefix tests whether the string s begins with prefix.
	fmt.Println(strings.HasPrefix(a, "he"))               // true
    // HasSuffix tests whether the string s ends with suffix.
	fmt.Println(strings.HasSuffix(a, "llo"))              // true
	fmt.Println(strings.Index(a, "ll"))                   // 2
	fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
	fmt.Println(strings.Repeat(a, 2))                     // hellohello
	fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo
	fmt.Println(strings.Split("a-b-c", "-"))              // [a b c]
	fmt.Println(strings.ToLower(a))                       // hello
	fmt.Println(strings.ToUpper(a))                       // HELLO
	fmt.Println(len(a))                                   // 5
	b := "你好"
	fmt.Println(len(b)) // 6
}

格式化

%v打印任意类型变量

%+v打印详细信息

%#v打印更加详细信息

package main

import "fmt"

type point struct {
	x, y int
}

func main() {
	s := "hello"
	n := 123
	p := point{1, 2}
	fmt.Println(s, n) // hello 123
	fmt.Println(p)    // {1 2}

	fmt.Printf("s=%v\n", s)  // s=hello
	fmt.Printf("n=%v\n", n)  // n=123
	fmt.Printf("p=%v\n", p)  // p={1 2}
	fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}

	f := 3.141592653
	fmt.Println(f)          // 3.141592653
	fmt.Printf("%.2f\n", f) // 3.14
}

JSON处理

  • 结构体每个字段第一个字母大写
  • json.Marshal()序列化
package main

import (
	"encoding/json"
	"fmt"
)

type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
    // 将 a 编码为 JSON 数据,并将结果存储在 buf 变量中
	buf, err := json.Marshal(a)
	if err != nil {
		panic(err)
	}
    // 打印 buf 变量的值,它是一个字节数组
	fmt.Println(buf)         // [123 34 78 97...]
    // 打印 buf 变量的字符串形式,它符合 JSON 格式
	fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
	
    // 将 a 编码为格式化后的 JSON 数据,并将结果存储在 buf 变量中
	buf, err = json.MarshalIndent(a, "", "\t")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf))

	var b userInfo
    // 将 buf 变量中的 JSON 数据解码为 b 变量,并将其存储在 b 变量中
	err = json.Unmarshal(buf, &b)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}

输出结果

[123 34 78 97 109 101 34 58 34 119 97 110 103 34 44 34 97 103 101 34 58 49 56 44 34 72 111 98 98 121 34 58 91 34 71 111 108 97 110 103 34 44 34 84 121 112 101 83 99 114 105 112 116 34 93 125]
{"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
{
        "Name": "wang",
        "age": 18,
        "Hobby": [
                "Golang",
                "TypeScript"
        ]
}
main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}

时间处理

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
	t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
	t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
	fmt.Println(t)                                                  // 2022-03-27 01:25:36 +0000 UTC
	fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
	fmt.Println(t.Format("2006-01-02 15:04:05"))                    // 2022-03-27 01:25:36
	diff := t2.Sub(t)
	fmt.Println(diff)                           // 1h5m0s
	fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
	t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)    // true
    //获取时间戳
	fmt.Println(now.Unix()) // 1648738080
}

数字解析

strconv包:字符串与数字转换方法

将字符串 "0x1000" 转换为 int64 类型的数值,使用了 ParseInt 函数,第二个参数是 0,表示自动检测字符串开头的前缀,如果是 "0x" 或者 "0X",则表示这是一个十六进制数,如果是 "0",则表示这是一个八进制数,否则默认为十进制数。

package main

import (
	"fmt"
	"strconv"
)

func main() {
	f, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(f) // 1.234

	n, _ := strconv.ParseInt("111", 10, 64)
	fmt.Println(n) // 111

	n, _ = strconv.ParseInt("0x1000", 0, 64)
	fmt.Println(n) // 4096

	n2, _ := strconv.Atoi("123")
	fmt.Println(n2) // 123

	n2, err := strconv.Atoi("AAA")
	fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}

进程信息

使用os.Args获取程序启动时传入的命令行参数,并打印。

使用os.Getenv获取环境变量PATH的值,并打印。

使用os.Setenv设置环境变量AA的值为BB。

根据操作系统不同,通过exec.Command执行grep或findstr指令,查找hosts文件中包含127.0.0.1的行,并获取输出。

  • 在Linux中使用grep指令

  • 在Windows中使用findstr指令

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	// go run example/20-env/main.go a b c d
	fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-					                                         //build3406981276/b001/exe/main a b c d]
	fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
	fmt.Println(os.Setenv("AA", "BB"))
	
	// linux
	// buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()  


	// windows
	cmd := exec.Command("findstr", "127.0.0.1", "C:\\Windows\\System32\\drivers\\etc\\hosts")
	buf, err := cmd.Output()
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf)) // 127.0.0.1       localhost
}