这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
Go语言的特点
- 高性能、高并发
- 语法简单
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
变量声明
-
可以按照以下方式声明变量:
var intVal int注意:var intVal int intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明var intVal int intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明 -
intVal := 1相等于:var intVal int intVal =1 -
多变量声明
//类型相同多个变量, 非全局变量 var vname1, vname2, vname3 type vname1, vname2, vname3 = v1, v2, v3 var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断 vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误 // 这种因式分解关键字的写法一般用于声明全局变量 var ( vname1 v_type1 vname2 v_type2 ) -
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。
go 控制流
- 判断语句 if-else
- 循环语句 for
- 条件分支语句 switch-case
go基本数据类型、函数、字符串操作
-
数组
-
slice 切片 append结果需要赋值回原来的切片
s = append(s, "d")不同于 python,go 切片不支持负数索引
-
map,查看 map 中是否有某元素时,用两个值接收
m := make(map[string]int) m["one"] = 1 m["two"] = 2 r, ok := m["unknow"] fmt.Println(r, ok) // 0 false -
range 快速遍历 slice 与 map slice 返回两个值: index,value map 返回 key,value
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 } } -
函数参数相同类型可以只在最后一个参数后面指定类型 函数可以返回多个返回值,在业务中,第一个值返回需要返回的值,第二个值返回错误信息
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 := add2(1, 2) fmt.Println(res) // 3 v, ok := exists(map[string]string{"a": "A"}, "a") fmt.Println(v, ok) // A True } -
go 指针 类似c/c++ 中的指针,可以用于在函数内部修改变量的值
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 } -
结构体:带类型的字段的集合 定义以及使用一个结构体:
type struct_name struct { var1 var1_type var2 var2_type } // 例子 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 }作为函数参数,可以修改结构体,避免某些大结构体拷贝的开销
-
结构体方法: 注意这里的方法与前面的方法的不同之处。 注意:编译器会隐式的获取变量的地址
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 类型的参数,表示函数具有错误处理;需要返回值时,返回两个值,第一个是要返回的值,第二个是错误信息
// user 为前面定义的结构体 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") } // 调用函数 if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil { fmt.Println(err) // not found return } else { fmt.Println(u.name) } -
go 字符串操作 go 标准库 strings 用例: 注意 replace 用法
func Replace(s, old, new string, n int) string返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串。func main() { 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 // 子串在字符串中的开始 index 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 } -
字符串格式化 fmt.Printf 直接使用 %v 打印变量的值,%+v 会打印结构体字段的名称,%#v 会打印整个结构体的类型名称以及字段名称和值
// 结构体定义 type point struct { x, y int } p := point{1, 2} 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} -
json操作 在库
"encoding/json"序列化: 结构体的字段第一个字母大写时,使用json.Marshal将其序列化,返回两个参数,第一个为序列化结果,第二个为错误信息。打印序列化结果时,将序列化结构转化为 string 可以打印出 json 格式的文本。 可以在字段末尾加上一个 json 的 tag 表示序列化后的键。 格式化输出:json.MarshalIndent()方法 反序列化: 使用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 包 注意格式化
t.Format("2006-01-02 15:04:05")layout直接就是2006-01-02 15:04:05将字符串转化为日期:t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36") -
字符串与数字的转换 在 strconv 包之中
strconv.ParseInt(s string, base int, bitSize int)base 是进制,0自动推断;bitSize是位数 -
字符串与十进制数的快速转换
n2, _ := strconv.Atoi("123") fmt.Println(n2) // 123 n2, err := strconv.Atoi("AAA") fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
实战
- 猜谜游戏:如何生成随机数,读取用户输入以及输出结果,函数控制流的使用
- 在线词典:抓包,使用代码生成工具
- socks5 协议:编写 socks5 代理服务器,contex 的使用,goroutine 的使用
总结
在熟悉其他语言的前提下,学习go语言的基础知识还是比较简单的。