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 接口。当我们将 BodyStdout 这两个值传给 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 的内容。

希望这两个小程序展示出接口的好处,以及标准库内部是如何使用接口的。下一步,让我们看一下实现接口的细节。

results matching ""

    No results matching ""