本文为系列文章,Go 基础语法可回顾上篇 三天刷完 Go by Example(上)
本篇是一些 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 语句,再次运行查看
当然,你也可以试下删除 file 文件夹,之后运行试试
字面意 Panic,「恐慌」性的异常,代表比较严重的问题,通常表示正常运行中必不可出现的错误,或者不准备处理的错误。
panic 可通过 recover 恢复,更多详见 https://golang.org/doc/effective_go#errors
Go 的
runtime
代码中很多地方都调用了panic
函数,像是数组下标越界、空指针等,平时应该很常见了!!!注意,与某些使用 exception 处理错误的语言不同, 在 Go 中,通常会尽可能的使用返回值来标示错误。
43 Defer
Defer 用于确保程序在执行完成后,会调用某个函数,一般是执行清理工作。 Defer 的用途跟其他语言的
ensure
或finally
类似。另外,
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
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 文件
ioutill.WriteFile
来把字符串直接写入文件(0644 为 FileMode)
os.Create
创建空文件并打开,!习惯性的操作:立即使用 defer 调用文件的Close
- 字节切片方式写入文件(10 为换行符)
f.WriteString
方法写入字符串
58 Reading Files
改写了示例读取内容,复用了示例 59 写入的文件
我们了解了 Golang 处理文件读、写的方式,接下来,我们看看它在
stdin
和stdout
流中的应用
60 Line Filters
运行后输入任意,查看输出打印
用带缓冲的 scanner 包装无缓冲的
os.Stdin
, 这为我们提供了一种方便的Scan
方法, 将 scanner 前进到下一个令牌
(默认为:下一行)
61 File Paths
filepath 包为 文件路径 ,提供了方便的跨操作系统的解析和构建函数
62 Directories
注释掉删除 subdir 文件夹的代码,可查看创建的文件夹及其下面的文件
对于操作文件系统中的 目录 ,Go 提供了几个非常有用的函数
63 Temporary Files and Directories
创建临时文件最简单的方法是调用
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
使用了run
和hello.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
go
和git
这种命令行工具,都有很多的 子命令 。 并且每个工具都有一套自己的 flag,比如:go build
和go get
是go
里面的两个不同的子命令。flag
包让我们可以轻松的为工具定义简单的子命令
$ ./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
handlers 是 net/http
服务器里面的一个基本概念。 handler 对象实现了 http.Handler
接口。 编写 handler 的常见方法是,在具有适当签名的函数上使用 http.HandlerFunc
适配器
handler 函数有两个参数,http.ResponseWriter
和 http.Request
。 response writer 被用于写入 HTTP 响应数据,这里我们简单的返回 “hello\n”
handler 函数有两个参数,http.ResponseWriter
和 http.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
可以立即以给定的状态退出程序