Go 标准库


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)

许多程序都需要处理或者发布数据,不管这个程序是要 使用数据库,进行 网络调用,还是 与分布式系统打交道

常用的标准数据格式是 JSONXML

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 的操作系统 伟大的一个原因是,一个程序的输出可以是另一个程序的输入 这一理念

这类操作系统创建了一系列的简单程序,每个程序只做一件事,并把这件事做得非常好

之后,将这些程序组合在一起,可以创建一些脚本做一些很惊艳的事情。

这些程序使用 stdinstdout 设备作为通道,在 进程之间传递数据。

标准库的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 语言习惯的好方法。
Buy me a 肥仔水!