本篇笔记是笔者在第六届字节跳动青训营期间学习 Go 语言的语法笔记,记录了一些 Go 语言中常见的语法
1. 数据类型
1. 类型转化与编码
· Go 为强类型语言,即使是 int 系列,也需要进行显式强转【不像 C 语言一样支持自动运算强转】
var a int
var b int64
a = int64(b)
c := float32(b) / float32(a)
· Go 中的 rune 本质上为 int32,其是一种加强版的 byte,用来表示 unicode 编码,中文默认使用 3 个字节来进行表示。将含有中文的字符串 []rune 转换可以正确打印原字符串内容。值得一提的是,for range 对字符串直接操作也可以正确打印,举例:
str := "你好,世界"
fmt.Println("normal print:")
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i])
}
fmt.Println()
fmt.Println("for_range print:")
for _, v := range str {
fmt.Printf("%c", v)
}
fmt.Println()
fmt.Println("convert into []rune print:")
runes := []rune(str)
for i := 0; i < len(runes); i++ {
fmt.Printf("%c", runes[i])
}
fmt.Println()
输出结果如下:
normal print:
ä½ å¥½ï¼ä¸ç
for_range print:
你好,世界
convert into []rune print:
你好,世界
2.数组、切片、结构体和接口
· Go 中数组(array)和结构体(struct)是值类型,切片(slice)和接口(interface)都是引用类型,使用类型名或接口名 + {} 可以直接开辟空间。Go中开辟空间实在堆区还是栈区是根据上下文由编译器自动推导的。笔者目前从未遇到任何因为堆区栈区开辟所导致的问题。切片和结构体举例如下:
sliceEmpty, sliceInit := []int{}, []int{1, 2, 3}
type structDefault struct {
name string
age int
}
stu1 := structDefault{"jack", 18}
stu2 := structDefault{name: "mary", age: 19}
· 切片底层实现有三个域:引用"数组"位置,切片大小,切片容量。一般会初始化切片空间以达到像其他编程语言中直接对数组的使用,示例如下。切片容量一般不需要考虑
func solution(n int, something []int) {
nums := make([]int, n)
for i := 0; i < n; i++ {
nums[i] = something[i] // len(somthing) >= n
}
}
· 切片常用 append() 进行动态扩充,有趣的是:当 append() 方法传入的切片为 nil 时,其会十分“智能”地自动开辟空间,并作为该切片的指向空间
· 空结构体为 struct{},可以将其理解成一种特殊的数据类型:结构体中的 bool 类型,在特殊的场合中使用最小的花销起到标记作用,常见于哈希存储中,示例如下:
type student struct {
name string
age int
}
func solution(stus []student) {
nameExistTable := map[student]struct{}{}
for _, v := range stus {
if _, ok := nameExistTable[v]; !ok {
nameExistTable[v] = struct{}{}
}
}
}
· 空接口为 interface{},由于其中没有任何方法,因此可以将所有类型(包括自定义类型)视为 interface{} 类型。通常可以定义空接口切片:[]interface{},来存储不同的数据类型
2. 文件操作
· Go 语言中打开文件并返回文件指针 *File 通常会用到 os 包下的两个函数,分别为 os.Open() 和 os.OpenFile()
os.Open()
func Open(name string) (*File, error)
os.Open() 只能以只读模式打开文件,并且文件打开后无法进行写入操作。
os.OpenFile()
func OpenFile(name string, flag int, perm FileMode) (*File, error)
-
参数 flag: 设置打开文件的模式,使用常量与或可以进行设置打开模式,如 os.O_RDONLY | os.O_CREATE
-
参数 perm: 设置文件的权限,其为一个八进制数,而 Go 中八进制数的表示以 0 开头。三位依此表示文件所有者,组和其他用户。比如 0755 的含义如下:
- 7 == 4 + 2 + 1,从左至右依此为 r, w, x (读,写,执行),表示文件所有者拥有读、写和执行权限
- 5 == 4 + 1,表示组和其他用户没有写权限,只有读和执行权限
3. 命令行参数使用
os.Args []string
在运行 go 程序时可以对其传入参数,存储在 os.Args 字符串切片中,其类型为 []string,注意:os.Args[0] 保存的不是传入的参数,具体如下:
func main() {
fmt.Println("There is", len(os.Args), "args in the using...")
for i, v := range os.Args {
fmt.Printf("[%v] arg is %v\n", i + 1, v)
}
}
输出结果:
bash: go build -o fileArgs.exe fileArgs.go
bash:./fileArgs.exe first 01 23 127.0.0.1
There is 5 args in the using...
[1] arg is ./fileArgs.exe
[2] arg is first
[3] arg is 01
[4] arg is 23
[5] arg is 127.0.0.1
flag 包
为了使得命令行传入的参数支持乱序、指定输入与默认值,Go 中提供了 flag 包来实现这一步骤,具体示例如下:
func main() {
var usr, pwd, ip string
var port int
flag.StringVar(&usr, "u", "", "user name")
flag.StringVar(&pwd, "p", "", "password")
flag.StringVar(&ip, "h", "127.0.0.1", "host ip")
flag.IntVar(&port, "P", 3306, "connect port")
flag.Parse()
allParse := []interface{}{usr, pwd, ip, port}
for _, v := range allParse {
fmt.Println(v)
}
}
输出结果如下:
./test.exe -p 123 -P 8080 -u anonym -h 224.1.1.2
anonym
123
224.1.1.2
8080
flag.StringVar() 与 flag.IntVar() 方法表明命令行传入的值 go 程序应该以怎样的“视角”来进行看待。以flag.StringVar()为例,参数说明如下:
func flag.StringVar(p *string, name string, value string, usage string)
p *string: 解析后存储的位置
name string: 调用者通过 -[name] 指定传入参数名
value string: 默认值,用于调用者未传参数时
usage string: 用于描述该参数,举例如下:
./test.exe -p 123 -P 8080 -u anonym -h
flag needs an argument: -h
Usage of ./test.exe:
-P int
connect port (default 3306)
-h string
host ip (default "127.0.0.1")
-p string
password
-u string
user name