变量var
go 是一种强变量类型的语言,常见的数据类型包括bool型、int型、float型等
有两种变量声明方式:
① var name (*) = ** -- var + 变量名 + 变量类型 + = + 初始化的值
(变量类型可以不写,而是自动推导)
(可以不用手动初始化,直接 var + 变量名 + 变量类型 使用默认初始化)
② 使用 变量 := 值 -- 变量名 + := 值
如 f := float32(x)
注意:go中const变量的具体变量类型,如果不给出,也是可以通过自动推导得到(和C/C++不同)
遍历 for
go 不存在 while 循环,只有这一种简单的 for 循环,同样三段,但不需要类似 C/C++ 那样添加括号
for j := 7; j < 9; j++ {
fmt.Println(j)
}
条件判断 if
同 C/C++ 一样,但是不需要加括号,而且必须跟花括号,不能没有花括号写在一行
num := 9
if num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
条件判断 switch
同 C/C++ 一样,但是首先 switch 紧接着的判断不用加括号,其次每个 case 可以不用写break来表示结束,执行完一个 case 就结束,这里和 C/C++ 继续跑完后面所有的 case 是不同的
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")
}
静态数组 array
因为长度固定,所以需要注意定义方式
var a [5]int
var b [2][3]int
c := [5]int{1, 2, 3, 4, 5}
动态切片 slice
注意动态切片的创建方法,通过 make 来创建切片,而且创建的动态切片可以像 python 一样支持切片操作,只不过不可以接受负值索引,slice原理是存储了一个长度、容量、指向数组的指针,可以参考 STL 中的 vector,当长度超过容量时,也会发生扩容,从而返回一个指向新数组的指针
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
s = append(s, "d")
s = append(s, "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
本身存储 key 是无序的,参照 STL 中的 unordered_map,注意定义方式即可
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
r, ok := m["unknow"]
delete(m, "one")
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
遍历 range
主要用于遍历数组和 map 时可以迅速遍历,比如对于数组可以得到索引以及索引所在的值,对于 map 可以得到键值对 key-value
nums := []int{2, 3, 4}
for i, num := range nums {
***
}
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
***
}
for k := range m {
***
}
函数 func
注意定义的方式,参数类型,返回类型,这些类型都是后置的
golang里面的函数原生支持多个返回值,真正的业务逻辑中一般函数都包含2个返回值,一个是真正的函数返回值,一个是错误信息
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
}
指针 point
和 C/C++ 使用方法类似,使用 & 获取指针对象,使用 * 解引用操作具体存储的值
结构体 struct
定义和 C/C++ 使用方法类似,定义方法注意类型后置
type user struct {
name string
password string
}
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"
结构体方法和 C/C++ 存在差异,方法定义时需要指定具体结构体作为参数,其实类似于C++中的类的成员函数
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
}
错误处理 error
可以在函数定义中,返回值加上 error,在函数体中出现异常的情况就通过 error 返回,在外部可以通过简单的 if-else 进行错误的排查定位
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")
}
字符串操作 string
知道一些常用的函数即可,和 python 类似
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
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
字符串格式化,对比 C 标准库中 printf 函数的使用,注意可使用 %v 来忽略格式打印,使用 +v/#v 来得到更详细内容
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 进行序列化,序列化之后产生 Byte 数组,字符串表示就是一个 json 字符串,其中默认序列化之后的字符串风格是大写字母开头,而不是下划线或者小写开头,如果有类似需求,可以在结构体对应字段后面加上 json tag 来修改默认序列化风格。同样的,反序列化是将 Byte 数组变为原始结构体数据,使用json.Unmarshal 函数实现
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
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"}}
}
时间处理 time
了解时间常用的函数即可
time.Now() // 获取当前时间
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC) // 构造时间
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 获取时间具体某个值
fmt.Println(t.Format("2006-01-02 15:04:05")) // 按照给定格式输出时间
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
diff := t2.Sub(t) // 计算时间间隔,默认为年月日时分秒
fmt.Println(diff.Minutes(), diff.Seconds()) // 统一为分,统一为秒
t, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36") // 按照格式(前者)将对应时间(后者)构造新的时间对象
fmt.Println(t1 == t2) // 比对时间
now.Unix() // 获取时间戳
数字解析 strconv
了解常用的一些字符串到基础数据类型的转换即可
f, _ := strconv.ParseFloat("1.234", 64)
n, _ := strconv.ParseInt("111", 10, 64)
n, _ = strconv.ParseInt("0x1000", 0, 64)
n2, _ := strconv.Atoi("123")
进程信息 env
fmt.Println(os.Args) // 获取运行时的参数
fmt.Println(os.Getenv("PATH")) // 获取系统环境变量 PATH 信息
fmt.Println(os.Setenv("AA", "BB")) // 设置环境变量 AA = BB
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput() // 执行命令得到输出