5.4.1 标准库
我们先来看一个示例程序,这个程序实现了流行程序curl的功能,如代码清单5-34所示。
代码清单5-34 listing34.go
01 // 这个示例程序展示如何使用io.Reader和io.Writer接口
02 // 写一个简单版本的curl程序
03 package main
04
05 import (
06 "fmt"
07 "io"
08 "net/http"
09 "os"
10 )
11
12 // init在main函数之前调用
13 func init() {
14 if len(os.Args) != 2 {
15 fmt.Println("Usage: ./example2 <url>")
16 os.Exit(-1)
17 }
18 }
19
20 // main是应用程序的入口
21 func main() {
22 // 从Web服务器得到响应
23 r, err := http.Get(os.Args[1])
24 if err != nil {
25 fmt.Println(err)
26 return
27 }
28
29 // 从Body复制到Stdout
30 io.Copy(os.Stdout, r.Body)
31 if err := r.Body.Close(); err != nil {
32 fmt.Println(err)
33 }
34 }
代码清单5-34展示了接口的能力以及在标准库里的应用。只用了几行代码我们就通过两个函数以及配套的接口,完成了curl程序。在第23行,调用了 http
包的 Get
函数。在与服务器成功通信后, http.Get
函数会返回一个 http.Response
类型的指针。 http.Response
类型包含一个名为 Body
的字段,这个字段是一个 io.ReadCloser
接口类型的值。
在第30行, Body
字段作为第二个参数传给 io.Copy
函数。 io.Copy
函数的第二个参数,接受一个 io.Reader
接口类型的值,这个值表示数据流入的源。 Body
字段实现了 io.Reader
接口,因此我们可以将 Body
字段传入 io.Copy
,使用Web服务器的返回内容作为源。
io.Copy
的第一个参数是复制到的目标,这个参数必须是一个实现了 io.Writer
接口的值。对于这个目标,我们传入了 os
包里的一个特殊值 Stdout
。这个接口值表示标准输出设备,并且已经实现了 io.Writer
接口。当我们将 Body
和 Stdout
这两个值传给 io.Copy
函数后,这个函数会把服务器的数据分成小段,源源不断地传给终端窗口,直到最后一个片段读取并写入终端, io.Copy
函数才返回。
io.Copy
函数可以以这种工作流的方式处理很多标准库里已有的类型,如代码清单 5-35所示。
代码清单5-35 listing35.go
01 // 这个示例程序展示bytes.Buffer也可以
02 // 用于io.Copy函数
03 package main
04
05 import (
06 "bytes"
07 "fmt"
08 "io"
09 "os"
10 )
11
12 // main是应用程序的入口
13 func main() {
14 var b bytes.Buffer
15
16 // 将字符串写入Buffer
17 b.Write([]byte("Hello"))
18
19 // 使用Fprintf将字符串拼接到Buffer
20 fmt.Fprintf(&b, "World!")
21
22 // 将Buffer的内容写到Stdout
23 io.Copy(os.Stdout, &b)
24 }
代码清单5-35展示了一个程序,这个程序使用接口来拼接字符串,并将数据以流的方式输出到标准输出设备。在第14行,创建了一个 bytes
包里的 Buffer
类型的变量 b
,用于缓冲数据。之后在第17行使用 Write
方法将字符串 Hello
写入这个缓冲区 b
。第20行,调用 fmt
包里的 Fprintf
函数,将第二个字符串追加到缓冲区 b
里。
fmt.Fprintf
函数接受一个 io.Writer
类型的接口值作为其第一个参数。由于 bytes.Buffer
类型的指针实现了 io.Writer
接口,所以可以将缓存 b
传入 fmt.Fprintf
函数,并执行追加操作。最后,在第23行,再次使用 io.Copy
函数,将字符写到终端窗口。由于 bytes.Buffer
类型的指针也实现了 io.Reader
接口, io.Copy
函数可以用于在终端窗口显示缓冲区 b
的内容。
希望这两个小程序展示出接口的好处,以及标准库内部是如何使用接口的。下一步,让我们看一下实现接口的细节。