本文为系列文章,Go 基础语法可回顾上篇 三天刷完 Go by Example(上)

Github 代码:https://github.com/ftopia2020/go_by_examples

本篇是一些 Golang 具体开发场景的示例,包含以下内容:

  • 一些内置包的常用方法(如:sort, time, io, os, fmt, json, xml 等)
  • Panic
  • Defer
  • 数据解析
  • 文件读写
  • 单元测试
  • 命令行操作
  • HTTP 客户端/服务端 简单实现
  • 生成进程 / 执行进程
  • 信号
  • 退出

40 Sorting

sort 包实现了四种基本排序算法:插入排序、归并排序、堆排序和快速排序。 但是这四种排序方法是不公开的,它们只被用于 sort 包内部使用。sort 包会根据实际数据自动选择高效的排序算法 🐂

有时间的 TX 可以深入看下源码,排序的注释很清晰明了,还加了复杂度说明 👍

41 Sorting By Functions

使用自定义的函数排序:创建一个自定义类型, 为它实现 Interface 接口的三个方法(Len, Swap, Less), 然后对这个自定义类型的切片调用 sort.Sort 方法, 我们就可以通过任意函数对 Go 切片进行排序了。

42 Panic

尝试下在 /tmp 文件夹下面创建 file 文件夹,注释首个 panic 语句,再次运行查看

image-20210331104102666
image-20210331104102666

当然,你也可以试下删除 file 文件夹,之后运行试试

字面意 Panic,「恐慌」性的异常,代表比较严重的问题,通常表示正常运行中必不可出现的错误,或者不准备处理的错误。

panic 可通过 recover 恢复,更多详见 https://golang.org/doc/effective_go#errors

Go 的 runtime 代码中很多地方都调用了 panic 函数,像是数组下标越界、空指针等,平时应该很常见了

!!!注意,与某些使用 exception 处理错误的语言不同, 在 Go 中,通常会尽可能的使用返回值来标示错误。

43 Defer

Defer 用于确保程序在执行完成后,会调用某个函数,一般是执行清理工作。 Defer 的用途跟其他语言的 ensurefinally 类似。

另外,recover 只能在 defer 语句中使用, 直接调用 recover 是无效的

44 Collection Functions

我们经常需要程序对数据集合执行操作, 例如选择满足给定条件的全部 item, 或通过自定义函数将全部 item 映射到一个新的集合。

在其它语言中,通常会使用泛型数据结构和算法。 但 Go 不支持泛型,如果你的程序或者数据类型有需要,通常的做法是提供函数集。

这是一些 strings 切片的组合函数示例。 你可以使用这些例子来构建自己的函数。 注意,在某些情况下,最简单明了的方法是: 直接内联操作方法集,而不是创建并调用帮助函数。

45 String Functions

strings 包提供了不少内置字符串处理方法,详见 https://golang.org/pkg/strings/

你可以使用类似 import 别名以及自定义 var 的方法简化重复代码的使用

46 String Formatting

Go 在传统的 fmt.Printf 中对字符串格式化提供了优异的支持;详见 https://golang.org/pkg/fmt/

47 Regular Expressions

Go 提供了内建的正则表达式支持。 这儿有一些在 Go 中与 regexp 相关的常见用法示例。

48 Json

Go 提供内建的 JSON 编码解码(序列化反序列化)支持, 包括内建及自定义类型与 JSON 数据之间的转化。

如果想要获取更多的信息, 请查阅 JSON and Go 博文 和 JSON package docs

另,使用 “先转换成json序列json.Marshal,再解析json.Unmarshal序列” 的方式,是实现对象的深拷贝(克隆)的一种方式。

!!但是,虽然基本上能够应对大部分的需求,缺点则是性能低,且有各种限制,比如无法处理递归指针、无法处理 interface 类型数据,会丢失缺少大部分数据类型甚至精度。

49 XML

50 Time

Go 为时间(time)和时间段(duration)提供了大量的支持,详见 https://golang.org/pkg/time/

51 Epoch

如何用 Go 来实现?获取 Unix 时间 的秒数,毫秒数,或者微秒数

52 Time Formatting

Go 支持通过基于描述模板的时间格式化与解析

时间解析使用与 Format 相同的布局值,当输入的时间格式不正确时,Parse 会返回一个解析错误

53 Random Numbers

有关 Go 提供的其他随机数的信息, 请参阅 math/rand 包文档

54 Number Parsing

Golang 从字符串中解析数字的常规操作;更多详见:https://golang.org/pkg/strconv/

55 URL Parsing

net/url 包提供内置解析方法,之后的示例也会涉及到 Golang net 包的使用场景

56 SHA1 Hashes

你可以使用和上面相似的方式来计算其他形式的散列值。 例如,计算 MD5 散列,引入 crypto/md5 并使用 md5.New() 方法

注意,如果你需要密码学上的安全散列,你需要仔细的研究一下 加密散列函数

57 Base64 Encoding

Go 提供了对 base64 编解码 的内建支持;标准 base64 编码和 URL base64 编码的 编码字符串存在稍许不同(后缀为 +-), 但是两者都可以正确解码为原始字符串

59 Writing Files

这边调整了下示例顺序,先写入文件,再读取文件

示例中分别采用了两种方式来写 dat1 和 dat2 文件

  1. ioutill.WriteFile 来把字符串直接写入文件(0644 为 FileMode)

  2. os.Create 创建空文件并打开,!习惯性的操作:立即使用 defer 调用文件的 Close

    • 字节切片方式写入文件(10 为换行符)
    • f.WriteString 方法写入字符串

58 Reading Files

改写了示例读取内容,复用了示例 59 写入的文件

我们了解了 Golang 处理文件读、写的方式,接下来,我们看看它在 stdinstdout 流中的应用

60 Line Filters

运行后输入任意,查看输出打印

用带缓冲的 scanner 包装无缓冲的 os.Stdin, 这为我们提供了一种方便的 Scan 方法, 将 scanner 前进到下一个 令牌(默认为:下一行)

61 File Paths

filepath 包为 文件路径 ,提供了方便的跨操作系统的解析和构建函数

62 Directories

注释掉删除 subdir 文件夹的代码,可查看创建的文件夹及其下面的文件

对于操作文件系统中的 目录 ,Go 提供了几个非常有用的函数

63 Temporary Files and Directories

image-20210331163116666
image-20210331163116666

创建临时文件最简单的方法是调用 ioutil.TempFile 函数。 它会创建并打开文件,我们可以对文件进行读写。 函数的第一个参数传 ""ioutil.TempFile 会在操作系统的默认位置下创建该文件。

defer 删除该文件。 尽管操作系统会自动在某个时间清理临时文件,但手动清理是一个好习惯。

64 Testing

想要写出好的 Go 程序,单元测试是很重要的一部分。 testing 包为提供了编写单元测试所需的工具,写好单元测试后,我们可以通过 go test 命令运行测试。

更多有关单元测试内容,建议查阅 https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter09/09.1.html

以下示例调整了对应的文件名称:

  • 需要被测试的代码: intutils.go

  • 其对应的测试文件: intutils_test.go

为方便演示,例子的代码位于 main 包,实际上,单元测试的代码可以位于任何包下。 测试代码通常与需要被测试的代码位于同一个包下。

你可以尝试故意改变部分测试数据为错误的,再查看运行结果。

$ go test			# 运行测试
$ go test -v	# 运行测试 打印详细用例运行结果
# 更多信息,可运行 go help test 和 go help testflag 了解

有关单元测试,有几点需要记的:

  • 测试套件:名称以 _test.go 结尾,该文件包含 TestXxx 函数,通常将该文件放在与被测试文件相同的包中
  • t.Error* 会报告测试失败的信息,然后继续运行测试
  • t.Fail* 会报告测试失败的信息,然后立即终止测试

65 Command Lines

$ go build 

命令行参数 是指定程序运行参数的一个常见方式。例如,go run hello.go, 程序 go 使用了 runhello.go 两个参数

创建了新文件夹以保存该示例,并事先运行了 go build 编译了可执行二进制文件

下面看看更高级的使用标记的命令行处理方法

66 Command Line Flags

命令行标志 是命令行程序指定选项的常用方式。例如,在 wc -l 中, 这个 -l 就是一个命令行标志

Go 提供了 flag 包,支持基本的命令行标志解析

创建了新文件夹以保存该示例,并事先运行了 go build 编译了可执行二进制文件

$ go build 66_command-line-flags.go # 构建二进制文件
$ ./66_command-line-flags -word=opt -numb=7 -fork -svar=flag	# 给所有标志赋值,尝试运行构建的程序
$ ./66_command-line-flags -word=opt # 省略一个标志,自动设定默认值
$ ./66_command-line-flags -word=opt a1 a2 a3 # 尾随的位置参数可以出现在任何标志后面
$ ./66_command-line-flags -word=opt a1 a2 a3 -numb=7 # 注意,flag 包需要所有的标志出现位置参数之前(否则,这个标志将会被解析为位置参数)
$ ./66_command-line-flags -h 	# 使用 -h 或者 --help 标志来得到自动生成的这个命令行程序的帮助文本
$ ./66_command-line-flags -wat # 提供了一个没有使用 flag 包声明的标志, 程序会输出一个错误信息,并再次显示帮助文本

67 Command Line Subcommands

gogit 这种命令行工具,都有很多的 子命令 。 并且每个工具都有一套自己的 flag,比如: go buildgo getgo 里面的两个不同的子命令。 flag 包让我们可以轻松的为工具定义简单的子命令

image-20210331165314868
image-20210331165314868

$ ./67_command-line-subcommands foo -enable -name=joe a1 a2		# 调用 foo 子命令
$ ./67_command-line-subcommands bar -level 8 a1		# 试一下 bar 子命令
$ ./67_command-line-subcommands bar -enable a1		# bar 不接受 foo 的 flag(enable)

68 Environment Variables

环境变量 是一种向 Unix 程序传递配置信息的常见方式。 让我们来看看如何设置、获取以及列出环境变量

键的列表是由你的电脑情况而定的

如果我们在运行前设置了 BAR 的值,那么运行程序将会获取到这个值

69 HTTP Clients

Go 标准库的 net/http 包为 HTTP 客户端和服务端提供了出色的支持

在这个例子中,我们将使用它发送简单的 HTTP 请求

70 HTTP Servers

handlersnet/http 服务器里面的一个基本概念。 handler 对象实现了 http.Handler 接口。 编写 handler 的常见方法是,在具有适当签名的函数上使用 http.HandlerFunc 适配器

handler 函数有两个参数,http.ResponseWriterhttp.Request。 response writer 被用于写入 HTTP 响应数据,这里我们简单的返回 “hello\n”

handler 函数有两个参数,http.ResponseWriterhttp.Request。 response writer 被用于写入 HTTP 响应数据,这里我们简单的返回 “hello\n”

使用 http.HandleFunc 函数,可以方便的将我们的 handler 注册到服务器路由。 它是 net/http 包中的默认路由,接受一个函数作为参数

最后,我们调用 ListenAndServe 并带上端口和 handler。 nil 表示使用我们刚刚设置的默认路由器

从截图运行中,看到查看了本机的端口占用情况(如被其他应用占用,可更改对应端口),并且 curl 访问了 hello 路由

71 Context

有时,Go 程序需要生成其他的、非 Go 的进程, os/exec 包可实现运行外部命令

72 Execing Processes

时候,我们只想用其它(也许是非 Go)的进程,来完全替代当前的 Go 进程。 这时,我们可以使用经典的 exec 函数的 Go 的实现

73 Signals

有时候,我们希望 Go 可以智能的处理 Unix 信号。 例如,我们希望当服务器接收到一个 SIGTERM 信号时,能够优雅退出, 或者一个命令行工具在接收到一个 SIGINT 信号时停止处理输入信息。 我们这里讲的就是在 Go 中如何使用通道来处理信号。

74 Exit

使用 os.Exit 可以立即以给定的状态退出程序