Go 标准库
Go 标准库
是一组核心包,用来扩展和增强语言的能力, 这些包和语言绑在一起发布,标准库 本身是经过良好设计的,开发人员应该尽量利用这些标准库
标准库里的顶级目录和包:
安装Go,标准库的源代码
都会安装在$GOROOT/src/pkg 文件夹
中。
拥有标准库的源代码对Go 工具正常工作非常重要,类似godoc、gocode 甚至go build 这些工具,
都需要读取标准库的源代码才能完成其工作。
如果源代码没有安装在以上文件夹中,或者无法通过$GOROOT 变量访问,在试图编译程序时会产生错误。
作为Go 发布包的一部分,标准库的源代码 是经过 预编译
的。这些预编译后的文件,称作 归档文件(archive file)
,
可以在$GOROOT/pkg 文件夹中找到已经安装的各目标平台和操作系统的归档文件
归档文件可以让构建的速度更快。
但是在构建的过程中,没办法指定这些文件,所以没办法与别人共享这些文件。
Go 工具链知道什么时候可以使用已有的.a 文件,什么时候需要从机器上的源代码重新构建。
1 记录日志 log 包
日志
是一种找到这些bug,更好地了解程序工作状态的方法。
日志是开发人员的眼睛和耳朵,可以用来跟踪、调试和分析代码。
基于此,标准库提供了log 包
,可以对日志做一些最基本的配置。根据特殊需要,开发人员还可以自己定制日志记录器。
stdout 的设备
所有的操作系统上都有这种设备,这种设备的 默认目的地 是标准文本输出
。
默认设置下,终端会显示这些写到stdout 设备上的文本。这种单个目的地的输出用起来很方便。
不过你总会碰到需要 同时输出 程序信息
和 执行细节
的情况。
这些执行细节被称作 日志。当想要记录日志时,你希望能写到不同的目的地,这 样就不会将程序的输出和日志混在一起了。
stderr 的设备
这个设备 被创建为 日志的 默认目的地
如果想在程序运行时 同时看到 程序输出 和 日志,可以将终端配置为同时显示写到stdout 和stderr 的信息
如果用户的程序 只记录日志,没有程序输出,更常用的方式是将一般的日志信息写到 stdout,将错误或者警告信息写到 stderr
log 包的使用
// TRACE: 2009/11/10 23:00:00.000000 /tmpfs/gosandbox-/prog.go:14: message
package main
import (
"log"
)
func init() {
log.SetPrefix("TRACE: ")
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
}
func main() {
log.Println("message")
log.Fatalln("fatal message") // Fatalln 在调用Println()之后会接着调用os.Exit(1)
log.Panicln("panic message") // Panicln 在调用Println()之后会接着调用panic()
}
log 包有一个很方便的地方就是,这些日志记录器是多goroutine 安全
的。这意味着在多个goroutine 可以同时调用来自同一个日志记录器的这些函数,而不会有彼此间的写冲突。
标准日志记录器具有这一性质,用户定制的日志记录器也应该满足这一性质。
定制的日志记录器
要想创建一个定制的日志记录器,需要 创建一个Logger 类型值
。可以 给每个日志记录器 配置一个单独的目的地
,并 独立设置其前缀和标志
1 为4 个日志等级声明了4 个Logger 类型的指针变量
var (
Trace *log.Logger // 记录所有日志
Info *log.Logger // 重要的信息
Warning *log.Logger // 需要注意的信息
Error *l og.Logger // 非常严重的问题
)
2 创建每个Logger 类型的值并将其地址赋给每个变量的
Trace = log.New(ioutil.Discard,
"TRACE: ",
log.Ldate|log.Ltime|log.Lshortfile)
Info = log.New(os.Stdout,
"INFO: ",
log.Ldate|log.Ltime|log.Lshortfile)
Warning = log.New(os.Stdout,
"WARNING: ",
log.Ldate|log.Ltime|log.Lshortfile)
Error = log.New(io.MultiWriter(file, os.Stderr),
"ERROR: ",
log.Ldate|log.Ltime|log.Lshortfile)
func New ( out io.Writer, prefix string, flag int ) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}
第一个参数 out 指定了日志要写到的目的地。这个参数传入的值必须实现了io.Writer 接口。
第二个参数prefix 是之前看到的前缀,
而日志的标志则是最后一个参数。
log 包的实现,是基于对记录日志这个需求长时间的实践和积累而形成的。
-
将输出写到 stdout,将日志记录到stderr,是很多基于命令行界面(CLI)的程序的惯常使用的方法。**
-
不过如果你的程序只输出日志,那么使用stdout、stderr 和文件来记录日志是很好的做法。
标准库的log 包包含了记录日志需要的所有功能,推荐使用这个包。我们可以完全信任这 个包的实现,不仅仅是因为它是标准库的一部分,而且社区也广泛使用它。
2 数据的编码和解码 (encoding/json)
许多程序都需要处理或者发布数据
,不管这个程序是要 使用数据库,进行 网络调用,还是 与分布式系统打交道
常用的标准数据格式是 JSON
和 XML
。
JSON 现在比XML流行
主要是因为 使用JSON 需要处理的标签更少。而这就意味着网络传输时每个消息的数据更少,从而提升整个系统的性能。
而且,JSON 可以转换为BSON(Binary JavaScript Object Notation,二进制JavaScript 对象标记),进一步缩小每个消息的数据长度
1 解码 Umarshal 函数
package main
import (
"encoding/json"
"fmt"
"log"
)
// JSON 包含要反序列化的样例字符串
var JSON = `{
"name": "Gopher",
"title": "programmer",
"contact": {
"home": "..",
"cell": ".."
}
}`
func main() {
// 将 JSON 字符串反序列化到map 变量
var c map[string]interface{}
err := json.Unmarshal([]byte(JSON), &c) // Unmarshal 函数将JSON 文档解码到一个结构类型的值里
if err != nil {
log.Println("ERROR:", err)
return
}
fmt.Println("Name:", c["name"])
fmt.Println("Title:", c["title"])
fmt.Println("Contact")
fmt.Println("H:", c["contact"].(map[string]interface{})["home"])
fmt.Println("C:", c["contact"].(map[string]interface{})["cell"])
}
2 编码 MarshalIndent 函数
将Go 语言的map 类型的值或者结构类型的值转换为易读格式的JSON 文档,序列化(marshal)是指将数据转换为JSON 字符串的过程
// 这个示例程序展示如何序列化JSON 字符串
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
// 创建一个保存键值对的映射
c := make(map[string]interface{})
c["name"] = "Gopher"
c["title"] = "programmer"
c["contact"] = map[string]interface{}{
"home": "..",
"cell": "..",
}
// 将这个映射序列化到JSON 字符串
data, err := json.MarshalIndent(c, "", " ") / 三个参数 v interface{} , prefix 前缀 , indent 缩进
if err != nil {
log.Println("ERROR:", err)
return
}
fmt.Println(string(data))
}
3 输入和输出 ( io 包 )
类 UNIX 的操作系统
伟大的一个原因是,一个程序的输出可以是另一个程序的输入
这一理念
这类操作系统创建了一系列的简单程序,每个程序只做一件事,并把这件事做得非常好
。
之后,将这些程序组合在一起,可以创建一些脚本做一些很惊艳的事情。
这些程序使用 stdin
和 stdout
设备作为通道,在 进程之间传递数据。
标准库的io 包
这个包可以 以流的方式 高效处理数据,而不用考虑数据是什么,数据来自哪里,以及数据要发送到哪里的问题。
与 stdout 和 stdin 对应,这个包含有io.Writer
和io.Reader
两个接口。
所有实现了这两个接口的类型的值,都可以使用io 包提供的所有功能,也可以用于其他包里接受这两个接口的函数以及方法。
// 这个示例程序展示bytes.Buffer 也可以
// 用于io.Copy 函数
package main
import (
"bytes"
"fmt"
"io"
"os"
)
// main 是应用程序的入口
func main() {
var b bytes.Buffer // 创建了一个bytes 包里的Buffer 类型的变量b,用于缓冲数据
// 将字符串写入Buffer
b.Write([]byte("Hello")) // 使用Write 方法将字符串Hello 写入这个缓冲区b
// 使用Fprintf 将字符串拼接到Buffer
fmt.Fprintf(&b, "World!") // 调用fmt包里的Fprintf 函数,将第二个字符串追加到缓冲区b 里
/*fmt.Fprintf 函数接受一个io.Writer 类型的接口值作为其第一个参数。由于
bytes.Buffer 类型的指针实现了io.Writer 接口,所以可以将缓存b 传入fmt.Fprintf
函数,并执行追加操作,由于bytes.Buffer 类型的指针也实现了io.Reader 接口, io.Copy 函数可以用于在终端窗口显示缓冲区b 的内容。
// 将Buffer 的内容写到Stdout
io.Copy(os.Stdout, &b) // 再次使用io.Copy 函数,将字符写到终端窗口。
}
4 go标准库小结
-标准库有特殊的保证,并且被社区广泛应用。
-使用标准库的包会让你的代码更易于管理,别人也会更信任你的代码。
-100 余个包被合理组织,分布在38 个类别里。
-标准库里的log 包拥有记录日志所需的一切功能。
-标准库里的xml 和json 包让处理这两种数据格式变得很简单。
-io 包支持以流的方式高效处理数据。
-接口允许你的代码组合已有的功能。
-阅读标准库的代码是熟悉Go 语言习惯的好方法。