在 Go 语言中,IO 操作是非常重要的一个部分,Go 提供了很多内置的包来进行 IO 操作,其中最常用的是 io、bufio、bytes 这三个包,其中 io 包是最基本的 IO 操作包,它提供了一些基本的 IO 接口,这些接口包括了读、写、复制等操作。
io 包定位
在 Go 官方文档中,是这么解释 io 包的:
Package io provides basic interfaces to I/O primitives. Its primary job is to wrap existing implementations of such primitives, such as those in package os, into shared public interfaces that abstract the functionality, plus some other related primitives.
Because these interfaces and primitives wrap lower-level operations with various implementations, unless otherwise informed clients should not assume they are safe for parallel execution.
简单来说,Go语言的 io 包是用于处理输入和输出的标准库,提供了一系列接口和类型,可以用于读写文件、网络连接、内存缓存和其他类型的数据流。它的主要工作是将现有的基本操作进行封装,提供共享的公共接口,抽象出其功能,同时提供了一些相关的基本操作。
常用 API
Copy
在使用 io 包时,我们可以使用 io.Copy() 函数将数据从一个 Reader 接口中复制到一个 Writer 接口中。
func Copy(dst Writer, src Reader) (written int64, err error)
其中,dst 表示目标 Writer 接口,src 表示源 Reader 接口。Copy() 函数会从 src 中读取数据并写入 dst 中,直到 src 的数据全部被读取完毕,或者在读取过程中出现了错误。在 Copy() 函数执行完毕后,它会返回已经复制的字节数和可能发生的错误。例如:
package main
import (
"bytes"
"io"
"os"
)
func main() {
file, err := os.Create("test.txt")
if err != nil {
panic(err)
}
defer file.Close()
data := []byte("hello world")
reader := bytes.NewReader(data)
_, err = io.Copy(file, reader)
if err != nil {
panic(err)
}
}
在上面的例子中,我们使用 os.Create() 函数创建了一个名为 test.txt 的文件,并向其中写入了字符串 hello world。首先,我们将字符串 hello world 转换为一个字节切片 data,然后使用 bytes.NewReader() 函数将其转换为一个 Reader 接口。接下来,我们将 file 和 reader 传递给 io.Copy() 函数,以将 reader 中的数据复制到 file 中。
CopyN
CopyN 方法是一种用于将数据从一个 Reader 复制到一个 Writer 的方法,并且它可以指定要复制的字节数。它的定义如下:
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
其中,dst 是一个 Writer 接口,src 是一个 Reader 接口,n 是要复制的字节数。这个方法会从 src 中读取 n 个字节的数据,并将其写入到 dst 中。
如果 src 中的数据不足 n 个字节,则 CopyN 方法会返回一个 io.EOF 错误,同时它也会返回已经复制的字节数。
例如,下面的代码展示了如何使用 CopyN 方法从一个文件中复制前 10 个字节到另一个文件中:
package main
import (
"fmt"
"io"
"log"
"os"
)
func main() {
src, err := os.Open("source.txt")
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.Create("destination.txt")
if err != nil {
log.Fatal(err)
}
defer dst.Close()
written, err := io.CopyN(dst, src, 10)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Copied %d bytes", written)
}
这个例子首先打开一个名为 source.txt 的文件,然后创建一个名为 destination.txt 的文件。然后,它使用 CopyN 方法将前 10 个字节从源文件复制到目标文件中。最后,它输出已经复制的字节数。
ReadFull
io.ReadFull() 函数可以从一个 Reader 接口中读取指定长度的数据,直到数据全部被读取完毕或者出现了错误:
func ReadFull(r Reader, buf []byte) (n int, err error)
其中,r 表示源 Reader 接口,buf 表示读取的数据需要写入的字节切片。ReadFull() 函数会一直从 r 中读取数据,直到 buf 中的所有字节都被填满,或者在读取过程中出现了错误。在 ReadFull() 函数执行完毕后,它会返回实际读取的字节数和可能发生的错误。例如:
package main
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
panic(err)
}
defer file.Close()
data := make([]byte, 5)
_, err = io.ReadFull(file, data)
if err != nil {
panic(err)
}
fmt.Println(string(data)) // output: "hello"
}
在上面的例子中,我们使用 os.Open() 函数打开了一个名为 test.txt 的文件,并读取了其中的前五个字节。首先,我们使用 make() 函数创建了一个长度为 5 的字节切片 data,然后将其传递给 io.ReadFull() 函数。接下来,ReadFull() 函数会从 file 中读取五个字节的数据,并将其填满到 data 中。最后,我们将 data 转换成字符串输出到控制台上。