标准库之input&output
1、基本的io接口
在 io 包中最重要的是两个接口:Reader 和 Writer 接口。
1.1Reader接口
Reader 接口的定义如下:
type Reader interface { Read(p []byte) (n int, err error) }
Read 将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len(p)) 以及任何遇到的错误。即使 Read 返回的 n < len(p),它也会在调用过程中占用 len(p) 个字节作为暂存空间。若可读取的数据不到 len(p) 个字节,Read 会返回可用数据,而不是等待更多数据。
当 Read 在成功读取 n > 0 个字节后遇到一个错误或 EOF (end-of-file),它会返回读取的字节数。它可能会同时在本次的调用中返回一个non-nil错误,或在下一次的调用中返回这个错误(且 n 为 0)。 一般情况下, Reader会返回一个非0字节数n, 若 n = len(p) 个字节从输入源的结尾处由 Read 返回,Read可能返回 err == EOF 或者 err == nil。并且之后的 Read() 都应该返回 (n:0, err:EOF)。
调用者在考虑错误之前应当首先处理返回的数据。这样做可以正确地处理在读取一些字节后产生的 I/O 错误,同时允许EOF的出现。
场景举例:
func ReadFrom(reader io.Reader, num int) ([]byte, error) { p := make([]byte, num) n, err := reader.Read(p) if n > 0 { return p[:n], nil } return p, err } main(){ // 从标准输入读取 data, err = ReadFrom(os.Stdin, 11) // 从普通文件读取,其中 file 是 os.File 的实例 data, err = ReadFrom(file, 9) // 从字符串读取 data, err = ReadFrom(strings.NewReader("from string"), 12) }
1.2Writer接口
Writer 接口的定义如下:
type Writer interface { Write(p []byte) (n int, err error) }
Write 将 len(p) 个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。若 Write 返回的 n < len(p),它就必须返回一个 非nil 的错误。
场景举例:
//在使用 Go 语言进行 Web 开发时,http.ResponseWriter
是最基本的类型之一,它本身是一个 Interface 类,原型如下:
type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(int) }
//直接调用 Write() 写入一串 []byte func helloHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.Write([]byte("Hello World")) return } func main() { http.HandleFunc("/", helloHandler) http.ListenAndServe(":8080", nil) }
1.3实现了 io.Reader 接口或 io.Writer 接口的类型
围绕io.Reader/Writer
,有几个常用的实现:
- net.Conn, os.Stdin, os.File: 网络、标准输入输出、文件的流读取
- strings.Reader: 把字符串抽象成Reader
- bytes.Reader: 把
[]byte
抽象成Reader - bytes.Buffer: 把
[]byte
抽象成Reader和Writer - bufio.Reader/Writer: 抽象成带缓冲的流读取(比如按行读写)
2、ReaderAt 和 WriterAt 接口
2.1ReaderAt 接口
type ReaderAt interface { ReadAt(p []byte, off int64) (n int, err error) }
ReadAt 从基本输入源的偏移量 off 处开始,将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len(p))以及任何遇到的错误。
当 ReadAt 返回的 n < len(p) 时,它就会返回一个 非nil 的错误来解释 为什么没有返回更多的字节。在这一点上,ReadAt 比 Read 更严格。
即使 ReadAt 返回的 n < len(p),它也会在调用过程中使用 p 的全部作为暂存空间。若可读取的数据不到 len(p) 字节,ReadAt 就会阻塞,直到所有数据都可用或一个错误发生。 在这一点上 ReadAt 不同于 Read。
若 n = len(p) 个字节从输入源的结尾处由 ReadAt 返回,Read可能返回 err == EOF 或者 err == nil
若 ReadAt 携带一个偏移量从输入源读取,ReadAt 应当既不影响偏移量也不被它所影响。
可对相同的输入源并行执行 ReadAt 调用。
场景举例:
reader := strings.NewReader("Go语言中文网") p := make([]byte, 6) n, err := reader.ReadAt(p, 2) if err != nil { panic(err) } fmt.Printf("%s, %d\n", p, n)
2.2WriterAt 接口
type WriterAt interface { WriteAt(p []byte, off int64) (n int, err error) }
WriteAt 从 p 中将 len(p) 个字节写入到偏移量 off 处的基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。若 WriteAt 返回的 n < len(p),它就必须返回一个 非nil 的错误。
若 WriteAt 携带一个偏移量写入到目标中,WriteAt 应当既不影响偏移量也不被它所影响。
若被写区域没有重叠,可对相同的目标并行执行 WriteAt 调用。
场景举例:
file, err := os.Create("writeAt.txt") if err != nil { panic(err) } defer file.Close() file.WriteString("Golang中文社区——这里是多余") n, err := file.WriteAt([]byte("Go语言中文网"), 24) if err != nil { panic(err) } fmt.Println(n)
3、ReaderFrom 和 WriterTo 接口
3.1ReaderFrom 接口
ReaderFrom 的定义如下:
type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) }
ReadFrom 从 r 中读取数据,直到 EOF 或发生错误。其返回值 n 为读取的字节数。除 io.EOF 之外,在读取过程中遇到的任何错误也将被返回。
如果 ReaderFrom 可用,Copy 函数就会使用它。
ReadFrom 方法不会返回 err == EOF。
场景举例:
//将文件中的数据全部读取(显示在标准输出) file, err := os.Open("writeAt.txt") if err != nil { panic(err) } defer file.Close() writer := bufio.NewWriter(os.Stdout) writer.ReadFrom(file) writer.Flush()
3.2WriterTo接口
type WriterTo interface { WriteTo(w Writer) (n int64, err error) }
WriteTo 将数据写入 w 中,直到没有数据可写或发生错误。其返回值 n 为写入的字节数。 在写入过程中遇到的任何错误也将被返回。
如果 WriterTo 可用,Copy 函数就会使用它。
场景举例:
//将一段文本输出到标准输出 reader := bytes.NewReader([]byte("Go语言中文网")) reader.WriteTo(os.Stdout)
4、Seeker 接口
接口定义如下:
type Seeker interface { Seek(offset int64, whence int) (ret int64, err error) }
Seek 设置下一次 Read 或 Write 的偏移量为 offset,它的解释取决于 whence: 0 表示相对于文件的起始处,1 表示相对于当前的偏移,而 2 表示相对于其结尾处。 Seek 返回新的偏移量和一个错误,如果有的话。
场景举例:
//获取倒数第二个字符 reader := strings.NewReader("Go语言中文网") reader.Seek(-6, io.SeekEnd) r, _, _ := reader.ReadRune() fmt.Printf("%c\n", r)
5、Closer接口
type Closer interface { Close() error }
该接口比较简单,只有一个 Close() 方法,用于关闭数据流。
文件 (os.File)、归档(压缩包)、数据库连接、Socket 等需要手动关闭的资源都实现了 Closer 接口。