Go基本语法及案例 | 青训营笔记

133 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

一、本堂课重点内容

1.Go 语言简介

Go 语言入门

Go 语言实战

二、详细知识点介绍

Go语言简介

这一部分,主要介绍了 Go 语言的一些特点,其中,包括高性能、高并发,语法简单、学习曲线平缓,丰富的标准库,完善的工具链,静态链接,快速编译,跨平台以及垃圾回收的特性。

Go语言入门

这一部分讲的比较简单,主要从 Golang 的安装以及环境的搭建开始讲解,然后在此基础上,介绍了 Golang 的基础语法。下面将对其简单介绍:

变量

Go 语言是一门强类型语言,每一个变量都有类型,常见类型包括字符串、整数、浮点型、布尔型等,在 golang 中,字符串是内置类型,可以通过加号来拼接,也可以直接使用等号比较。

对于变量的声明方法主要有三种:

  • var i int 该方式指明变量 i 为 int 类型
  • var i = 1 该方式将 1 赋值给 i ,此处会对 i 的类型自动推导
  • i := 1 该方式更为简介,直接将 1 赋给 i ,也会自动推导

if-else

对于条件语句, golang 中的 condition 不用加括号,即使加了括号也会在保存时自动去除,除此之外,语句大括号左半部分必须与 if 位于同一行。

还有一点是,在 if 语句中,是可以使用赋值语句,来创建一个只在 if 内局部作用的变量。例如:

if num := 9; num < 0 {
    ...
}

循环

在 golang 中,只有 for 循环一种循环,与 C/C++ 的 for 循环类似,可以在 for 语句后面写上初始语句,条件语句以及自增语句。比较特殊的是可以省略初始和自增语句只写条件语句,甚至可以只写 for 而形成一个死循环。

for condition {
    ...
}

for {
    ...
}

switch

与 C/C++ 类似,不过更加强大,可以在 switch 后不加任何变量,而直接在 case 中写条件分支,这种比使用多个 if-else 逻辑更为清晰。还有一点是 golang 中的 switch 是不用 break 来结束当前 case 的, case 运行完自动会退出 switch 的。

switch {
    case a < b:
        ...
    default:
        ...
}

数组

golang 中的数组是定长的,不常用。

切片

golang 中的切片可以看成是不定长的数组,其可以像在 python 中一样对列表进行切片操作,更多的内容还是看书吧。

map

与其他语言的哈希或字典相对应,创建方法: make(map[string]int) ,这样就创建了一个 key 为 string , value 为 int 的 map 。

range

类似于其他语言中的迭代器

for k, v := range map_demo {
    ...
}

函数

golang 中的函数可以有多个返回值,且返回值通常写在参数列表的后面,左括号的前面

func f(argA int, argB int) int {
    ...
}

指针

golang 中使用指针可以在函数内部改变外部的值。对一个变量使用 & 来赋值给指针变量,使用 * 来对原始变量操作。

结构体

golang 中的结构体由 struct 关键字定义,对于首字母大写的关键字将会成为公有属性。使用 . 来访问成员以及成员函数,对于指针变量也适用( golang 的语法糖)。

结构体方法

在函数的 func 关键字后面使用 (var1 struct1)(var1 *struct1) 可以将该函数注册为成员函数,带指针与不带指针的区别在于要不要修改结构体。

错误处理

在函数返回时,通常将第二个返回值设为是否发生错误来传递是否工作正常,不同于 Java 的错误处理机制, golang 的错误处理非常的简单。

字符串操作

在标准库 strings 包中有很多常用的字符串处理函数,比如 Contains 判断是否包含字串等等。

字符串格式化

在标准库 fmt 包中有很多由于对字符串格式化的方法,比如常用的 PrintlnPrintf ,在 golang 中可以使用 %#v 来输出变量的详细信息。

JSON 处理

golang 中对于 JSON 的操作非常简单,对于一个已有的结构体,保证每个字段的首字母大写,然后就可以使用 json.Marshal 来序列化,变成一个 JSON 格式的字符串,同样对于一个 JSON 格式的字符串,可以使用 json.Unmarshal 来反序列化到一个结构体中。在结构体中可以使用 json tag 来标记序列化后的字段名。比如:

type user struct {
    Name string `json:"name"`
    ...
}

时间处理

主要使用 time 包来对时间处理,具体查看文档。

数字解析

主要使用 strconv 包进行处理,具体查看文档。

进程信息

主要使用 os 包中的 os.Args 来获取命令行参数。

三、实践练习例子:

课程中主要介绍了三个实践练习来帮助理解。分别是

  • 猜谜游戏
  • 在线词典
  • SOCKS5 代理

下面将从上述三个方面对实例进行介绍

1.猜谜游戏

知识点

  • 随机数
  • time 包的使用
  • bufio 包的使用
  • strconv 包的使用
  • strings 包的使用
  • os 包的使用

实现过程

通过 rand 随机生成一个数,然后从命令行读取一个数,之后与随机生成的数进行比较,若太大,则输出太大,太小则输出太小,直到玩家猜中该数。

关键点

  • 随机数的生成种子要使用变化的值,使用定值的话最后永远都是定值而不是随机值。

作业

使用 Scanf 来改进程序。代码如下:

func main() {
	maxNum := 100
	rand.Seed(time.Now().UnixNano())
	secretNumber := rand.Intn(maxNum)

	fmt.Println("Please input your guess")
	var guess int
	for {
		_, err := fmt.Scanf("%d\n", &guess)
		if err != nil {
			fmt.Println("An error occurred while reading input. Please try again", err)
			continue
		}
		fmt.Println("Your guess is ", guess)
		if guess > secretNumber {
			fmt.Println("Your guess is bigger than the secret number. Please try again")
		} else if guess < secretNumber {
			fmt.Println("Your guess is smaller than the secret number. Please try again")
		} else {
			fmt.Println("Correct, you Legend!")
			break
		}
	}
}

2.在线词典

知识点

  • 抓包
  • 代码生成
  • JSON 序列化与反序列化

实现过程

首先使用浏览器抓包获取翻译接口,然后将其拷贝为 cURL ,然后到网站 Convert curl commands to code 中将其转换为 golang 代码,之后将之前响应的响应体( JSON 格式)在 OKTools 中转换为嵌套的 golang 结构体,然后在代码中,将响应的响应体反序列化到结构体中,之后便可对结果进行输出控制。

关键点

  • 需要寻找可靠的翻译网站

作业

1.添加另一种翻译引擎的支持

代码见 两种翻译引擎

2.在上述基础上实现并行请求

代码见 并行请求

3.SOCKS5 代理

知识点

  • SOCKS5 协议原理
  • TCP Echo Server 的原理
  • goroutine 的使用
  • context 包的使用

实现过程

SOCKS5 协议是一种代理服务器协议,客户端通过代理来访问服务器的内容。首先浏览器和代理建立 TCP 连接,然后代理再和服务器 TCP 连接。大致分为四个阶段:握手、认证、请求、 relay 。此处实现过程中没有认证,故大致分为三部分,握手时协商好协议的版本号之类的信息,而请求阶段是浏览器向目的服务器发出请求,代理服务器接收到后与服务器建立TCP,然后返回响应。 relay 阶段主要是代理服务器对正常消息的转发与接受。

关键点

  • 理解 SOCKS5 原理
  • 理解 goroutine 的使用方法
  • 理解 context 的使用方法

四、课后个人总结:

课程所给出的几个实践练习例子很有趣,能够很好的将所学的内容运用到其中。对于翻译引擎的添加,我最先尝试添加有道翻译,不料其所返回的json太长,个人感觉性能不会很好。另外尝试了一些其他的之后,百度的有防范,最后还是选择了360翻译,虽然不是很出名,但是解析起来还是比较方便的。另外在添加并行请求时,之前看到有一种方法叫做WaitGroup,可以让goroutine在内部使用wg.Done()来将数值减一,最后main中使用wg.wait()来等待goroutine的结束,这样就可以在main结束之前将所有goroutine(main以外)运行完。