这是我参与「第五届青训营 」伴学笔记创作活动的第1天。主要内容为Go语言的基础语法以及3个实战项目。
1 Go语言特性
- 高性能、高并发
- 语法简单
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收机制
2 基础语法
2.1 变量声明
主要有两种声明方式,一种显示地指定变量类型,一种短声明。
var a int
a := 1
Go的基本变量类型分为值类型和引用类型两类。特别注意的是Go可以指定整形和浮点型的位数如 int64 int32 float32 float64,与c语言不同的是string为Go的基本变量类型,属于值类型,底层通过byte数组实现。
2.2 程序结构
与大多数程序设计语言一样,Go可以通过if else、switch case实现分支结构,for实现循环结构,需要注意的是Go没有while关键字,因此只能通过for语句实现while或do while的逻辑结构。
2.3 数组、切片、map及其遍历
数组和c语言概念基本一致,包括一段连续且固定的同类型变量的存储空间,不够灵活,实际应用较少。
切片底层通过动态数组来实现,有len、cap两个主要属性,len表示切片的实际长度,cap表示进程实际分配给切片的长度,使用内置的append函数可实现在切片后追加元素。当切片追加一个元素时len和cap相等,那么程序会给切片开辟一段新的更大的空间,将原空间的元素逐一拷贝到新的空间中,再追加元素。见如下代码:
slice1 := make([]int, 0, 1)
fmt.Println("len:", len(slice1), "cap:", cap(slice1)) //len:0 cap:1
slice1 = append(slice1, 1)
slice1 = append(slice1, 2)
fmt.Println("len:", len(slice1), "cap:", cap(slice1)) //len:2 cap:2
slice1 = append(slice1, 3)
fmt.Println("len:", len(slice1), "cap:", cap(slice1)) //len:3 cap:4
slice1 = append(slice1, 4)
slice1 = append(slice1, 5)
fmt.Println("len:", len(slice1), "cap:", cap(slice1)) //len:5 cap:8
可以看到似乎是以倍增的方式来动态的扩充数组的。
map类似python的dict,以key-value的方式来存储数据,底层是基于hashtable实现,冲突解决采用拉链法,注意Go的map是线程不安全的,因此如果多个线程对同一map进行操作的话需要加锁或者使用sync.map。
2.4 结构体、指针、函数
Go没有类这个概念,只能通过定义结构体来代替,Go的结构体可以绑定函数,依次来实现类中的方法。指针以及函数的概念与c语言基本一致。
3 实战
3.1 猜谜游戏
下面给出我使用fmt.Scanf来简化代码的实现
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
// fmt.Println("The secret number is ", secretNumber)
for {
fmt.Println("Please input your guess")
//使用Scanf简化代码
var guess int
for {
var input string
fmt.Scanf("%s\n", &input)
var err error
//需验证输入是否合法
if guess, err = strconv.Atoi(input); err != nil {
fmt.Println("Your input is illegal")
} else {
break
}
}
fmt.Println("You guess is", guess)
if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
3.2 在线字典
使用课上给的两个工具curlconverter和oktools可以很容易的实现接入另一个翻译API,这里我接入的是百度翻译的API,另外并行请求两个翻译引擎来提高相应速度的实现方式如下:
//并行请求,提高相应速度
ctx, cancel := context.WithCancel(context.Background())
go func() {
querytoBaidu(word)
cancel()
}()
go func() {
querytoCaiyu(word)
cancel()
}()
<-ctx.Done()
使用context.cancel()来控制程序流程。
3.3 socks5代理
使用Go实现一个Socks5 Server处理浏览器的请求,改部分只要弄懂Socks5协议的原理、每个阶段发送的报文以及报文每个字段的含义和返回的报文的格式便很容易实现。个人认为主要难点在于到对报文字节流的处理,以下是我总结的处理字节流的技巧:
- 使用两位8进制数来表示一个字节的值
- 声明一个
[]byte作为缓冲区buf,并且尽量复用改buf - 使用io标准库中的函数
ReadFull()等向buf中填充数据。