标准库提供了io包,里面以流的形式高效的处理数据,不同考虑数据是什么,来自什么地方,发送到哪里,包含io.Writer和io.Reader两个接口,只要实现了对应接口的函数,就可以使用io包中提供的所有功能
Writer和Reader接口
io包围绕着Writer和Reader接口来构建的
内部提供了足够的抽象
首先是io.Writer的声明
type Writer interface {
Writer(p [] byte)(n int,err error)
}
我们展示了io.Writer接口的声明,声明了唯一的方法Writer,就是接受一个byte切片,返回两个值,一个是写入的字节数,一个是err错误值
如果,返回的是 n<len(p)的时候,必须要返回某个非nil的error,
而且,Write不能改写切片中的数据,临时修改也不行
所以Write方法的实现需要能够支持写入传入的byte的全部数据,如果无法写入全部,就返回一个错误
然后是Reader接口的声明
type Reader interface {
Read(p [] byte)(n int,err error)
}
io.Reader接口声明了一个方法Read,接受一个byte,返回两个值,一个值是error错误值,另一个则是读出的数字
对于这个接口,Go的规定如下
第一,如果读到的字节数小于byte切片的长度,那么不要等待新的数据,而是直接返回已读的数据
第二,对于已经到达了可读的末尾的情况,我们可以返回最终的字节数,并且返回EOF作为错误,或者返回nil做为错误值
第三,我们调用Read的时候,如果返回了可读的字节数,先去处理读到的字节,然后处理错误值
第四条,就是不要返回0个可读字节的同时,返回nil作为错误值,如果没有读到值,就返回一个错误
那么我们看一下这些接口和io包的整合使用
使用到了bytes fmt os进行缓冲
拼接和写字符串到stuout
//展示不同的标准款的不同函数如何使用io.Writer接口的
package main
import (
“bytes”
“fmt”
“os”
)
func main() {
// 创建一个Buffer值,将一个字符串写入Buffer
// 实现io.Writer的Write方法
var b bytes.Buffer
b.Write([]byte(“Hello “))
// 传入一个Buffer地址作为io.Writer称为Fprintf的参数
fmt.Fprintf(&b, “World!”)
// 写到标准输出
b.WriteTo(os.Stdout)
}
模拟的标准的输入输出
使用os.stdout来将Hello World写到了终端窗口
比较感兴趣的是Fprintf函数,其函数声明如下
func Fprintf(w io.Writer,format string,a … interface{})(n int,err error)
第一个参数接收一个实现了io.Writer类型的值的地址,
然后我们的Buffer就实现了io.Writer的接口,传入就增加到了Buffer中
最后我们使用writeTo,写到了一个实现io.Write接口的值中
对于os.Stdout的值
var (
Stdin = NewFile(uintptr(syscall.Stdin), “/dev/stdin”)
Stdout = NewFile(uintptr(syscall.Stdout), “/dev/stdout”)
Stderr = NewFile(uintptr(syscall.Stderr), “/dev/stderr”)
)
NewFile返回一个给定的文件描述符和名字的新File
正好实现了io.Write接口
我们利用了都实现了Writer接口,利用标准库中的功能,讲这些组合在一起工作
然后就是利用这些os相关的工具,实现自己的curl工具
我们看下代码如下
//写一个简单版本的curl
package main
import (
“io”
“log”
“net/http”
“os”
)
func main() {
// r是对应的相应,r.Body是io.Reader
r, err := http.Get(os.Args[1])
if err != nil {
log.Fatalln(err)
}
// 创建文件来保存相应
file, err := os.Create(os.Args[2])
if err != nil {
log.Fatalln(err)
}
defer file.Close()
// 合并两个输出设备,进行写操作
dest := io.MultiWriter(os.Stdout, file)
// 读出响应的内容,进行拷贝
io.Copy(dest, r.Body)
if err := r.Body.Close(); err != nil {
log.Println(err)
}
}
基本的curl骨架如下,可以下载展示并保存任何的HTTP Get请求的内容,响应的结果同时写入文件和stdout,在同时写入的方面,我们使用了MultiWriter函数将打开的文件和stdout整合为一个io.Writer值,然后获取值,Copy到了文件和系统输出
利用io包已经提供的支持,以及http和os包已经实现了io.Writer和io.Reader接口类型的实现
解决了curl的问题
我们在io中找到了大量的支持不同功能的函数,通过实现了io.Writer和io.Reader接口类型的值进行调用,了解其可以让我们了解Go语言的设计