10.3 包io
在Go 语言中,io是一个重要的标准库包,提供了一系列内置接口和方法,用于处理输入、输出流操作。包io中包含的常用接口如表10-1所示。
表10-1 包io中包含的常用接口
接口名称 | 描述 |
io.Reader | 代表可以读取数据的对象,定义了一个 Read 方法 |
io.Writer | 代表可以写入数据的对象,定义了一个 Write 方法 |
io.Closer | 代表可以关闭的对象,定义了一个 Close 方法 |
io.Seeker | 代表可以定位的对象,定义了一个 Seek 方法 |
io.ReaderAt | 代表可以从指定位置读取数据的对象,定义了一个 ReadAt 方法 |
io.WriterAt | 代表可以从指定位置写入数据的对象,定义了一个 WriteAt 方法 |
注意:除了以上列出的接口之外,io 包还定义了一些其他接口,例如 io.ReadCloser、io.WriteCloser、io.ReadSeeker、io.WriteSeeker 等,这些接口都是由上述基本接口组合而成的。
10.3.1 io.Reader和io.Writer
在Go语言中,io.Reader和io.Writer是两个非常重要的接口。在这两个接口中提供了一些内置方法,用于在文件中实现读取或写入数据。
1. io.Reader
在Go语言中,接口io.Reader用于从流中读取数据。在下表10-2中列出了接口io.Reader中的内置方法的和功能信息。
表10-2 接口io.Reader中的内置方法的和功能信息
方法名称 | 方法签名 | 功能描述 |
Read | func (T) Read(b []byte) (n int, err error) | 从数据源中读取数据到缓冲区b中,返回实际读取的字节数和是否有错误发生。如果返回值的字节数小于缓冲区长度,则表示读取结束。 |
ReadAtLeast | func (T) ReadAtLeast(b []byte, min int) (n int, err error) | 从数据源中读取至少min个字节的数据到缓冲区b中,返回实际读取的字节数和是否有错误发生。如果返回值的字节数小于min,则表示读取不足。 |
ReadFull | func (T) ReadFull(b []byte, min int) (n int, err error) | 从数据源中读取指定长度的数据到缓冲区b中,返回实际读取的字节数和是否有错误发生。如果返回值的字节数小于指定长度,则表示读取不足。 |
表10-2中的方法都是通过实现了io.Reader接口的类型来调用的。我们可以根据自己的需要选择不同的方法来读取数据。
例如在下面的格式中,io.Reader接口定义了方法Read(),用于从数据源中读取数据。
type Reader interface {
Read(p []byte) (n int, err error)
}
其中,p表示读取数据的缓冲区,n表示实际读取的字节数,err表示是否有错误发生。如果err == io.EOF,表示已经读取到文件末尾。如果返回其他错误,则说明在读取过程中出现了错误。
假设有一个记事本文件“filename.txt”,里面的内容如图10-1所示。
图10-1 文件“filename.txt”中的内容
实例10-2:读取指定文件中的内容(源码路径:Go-codes\10\read.go)
实例文件read.go的具体实现代码如下所示。
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("filename.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
buf := make([]byte, 1024) // 创建读取数据的缓冲区
for {
n, err := file.Read(buf) // 从文件中读取数据到缓冲区
if n == 0 {
break // 读取结束
}
if err != nil && err != io.EOF {
fmt.Println("Error:", err)
return
}
fmt.Print(string(buf[:n])) // 输出缓冲区中的数据
}
}
对上述代码的具体说明如下:
- 首先通过函数os.Open()打开一个名为test.txt的文本文件,并将其赋值给file变量。
- 然后创建一个大小为1024字节的缓冲区来读取文件中的数据。
- 在for循环中,不断地从文件中读取数据到缓冲区中,并将实际读取的字节数保存到n变量中。如果读取到文件末尾,则退出循环;如果读取过程中发生了错误,则输出错误信息并退出程序。
- 最后,通过函数string()将缓冲区中的字节转换为字符串,并输出到控制台。
执行后会输出:
4月28日消息,天齐锂业公告,一季度实现营业收入114.49亿元,同比增长117.77%;净利润48.75亿元,同比增长46.49%;基本每股收益2.97元。
2. io.Writer写入数据
在Go语言中,接口io.Writer用于向流中写入数据。在下表10-3中列出了接口io.Writer中的内置方法的和功能信息。
表10-2 接口io.Writer中的内置方法的和功能信息
方法名称 | 方法签名 | 功能描述 |
Write | func (T) Write(b []byte) (n int, err error) | 将缓冲区b中的数据写入到数据源中,返回实际写入的字节数和是否有错误发生。如果返回值的字节数小于缓冲区长度,则表示写入结束。 |
WriteString | func (T) WriteString(s string) (n int, err error) | 将字符串s写入到数据源中,返回实际写入的字节数和是否有错误发生。如果返回值的字节数小于字符串长度,则表示写入结束。 |
WriteAt | func (T) WriteAt(b []byte, off int64) (n int, err error) | 将缓冲区b中的数据从指定位置off开始写入到数据源中,返回实际写入的字节数和是否有错误发生。如果返回值的字节数小于缓冲区长度,则表示写入结束。 |
表10-2中的方法都是通过实现了io.Writer接口的类型来调用的,我们可以根据自己的需要选择不同的方法来写入数据。
实例10-3:将某个文件中的内容复制到另外一个文件(源码路径:Go-codes\10\copy.go)
实例文件copy.go的具体实现代码如下所示。
import (
"fmt"
"io"
"os"
)
func main() {
source, err := os.Open("filename.txt") // 打开源文件
if err != nil {
fmt.Println("Error:", err)
return
}
defer source.Close()
destination, err := os.Create("destination.txt") // 创建目标文件
if err != nil {
fmt.Println("Error:", err)
return
}
defer destination.Close()
reader := io.Reader(source)
writer := io.Writer(destination)
_, err = io.Copy(writer, reader) // 将源文件中的数据复制到目标文件中
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Copy success!")
}
对上述代码的具体说明如下:
- 首先通过函数os.Open()打开了一个名为source.txt的文本文件,并将其赋值给变量source。
- 接着,通过函数os.Create()创建了一个名为destination.txt的记事本文件,并将其赋值给destination变量。
- 然后,分别创建了一个源文件的读取器和一个目标文件的写入器。
- 在函数io.Copy()中,将源文件中的数据复制到目标文件中,并打印输出复制成功的信息。
执行后会输出:
Copy success!
成功将文件filename.txt中的内容复制到文件destination.txt中,如图10-2所示。
图10-2 文件destination.txt中的内容
10.3.2 io.ReaderAt和io.WriterAt
在Go语言中,通过接口io.ReaderAt和io.WriterAt可以从指定位置读取/写入数据。
1. io.ReaderAt
接口io.ReaderAt定义了一个读取指定位置数据的方法,该接口适用于需要随机访问文件或其他数据源的情况。在接口io.ReaderAt中包含了一个内置函数ReadAt(),功能是从指定偏移量处读取数据到缓冲区中。函数ReadAt()的原型如下所示。
func (r ReaderAt) ReadAt(p []byte, off int64) (n int, err error)
- 参数p []byte:缓冲区,读取的数据将被写入到这个参数指向的字节数组中。
- 参数off int64:偏移量,从文件的哪个位置开始读取数据。
- 返回值:读取的字节数和可能出现的错误。
通过使用接口io.ReaderAt,可以更加灵活地访问文件或其他数据源中的数据。例如,在处理大文件时,可以使用接口io.ReaderAt分块读取数据,以减少内存占用,提高程序的性能。
2. io.WriterAt
接口io.WriterAt定义了一个向指定位置写入数据的方法,该接口适用于需要随机访问文件或其他数据源的情况。在接口io.WriterAt中包含了一个内置函数WriteAt(),功能是将缓冲区中的数据写入到指定偏移量的位置。函数WriteAt()的原型如下所示。
func (w WriterAt) WriteAt(p []byte, off int64) (n int, err error)
- p []byte:缓冲区,需要写入的数据存储在这个参数指向的字节数组中。
- off int64:偏移量,从文件的哪个位置开始写入数据。
- 返回值:为写入的字节数和可能出现的错误。
通过使用接口io.WriterAt,可以更加灵活地向文件或其他数据源中写入数据。例如,在分布式系统中,可以使用接口io.WriterAt将数据按照某种规则写入多个节点上,以实现高效的数据存储和访问。
假设文件example.txt中的内容如图10-3所示,在下面的实例中,使用接口io.ReaderAt和接口io.WriterAt将文件中指定位置的文本修改为新的内容。
图10-3 文件example.txt中的内容
实例10-4:将文件中指定位置的文本修改为新的内容(源码路径:Go-codes\10\mod.go)
实例文件mod.go的具体实现代码如下所示。
import (
"fmt"
"io"
"os"
)
func main() {
// 打开文件并创建读写器
file, err := os.OpenFile("example.txt", os.O_RDWR, 0666)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
// 读取原始文本
offset := int64(10) // 修改文本的起始位置
size := int64(5) // 修改文本的长度
buf := make([]byte, size)
n, err := file.ReadAt(buf, offset)
if err != nil && err != io.EOF {
fmt.Println("Error:", err)
return
}
fmt.Printf("Original text: %s\n", buf[:n])
// 写入新文本
newText := []byte("hello")
n, err = file.WriteAt(newText, offset)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Write %d bytes to file\n", n)
}
对上述代码的具体说明如下:
- 首先打开一个名为example.txt的记事本文件,并使用函数os.OpenFile()以读写方式打开,并指定了文件的字符编码格式为UTF-8。
- 然后,使用方法file.ReadAt()从文件的第10个字节处开始读取长度为5的文本内容,并将其赋值给buf变量。
- 接着,使用方法file.WriteAt()向文件的第10个字节处写入新文本"hello"。
- 最后,重新读取文件内容并输出。
执行后会输出:
Original text: 4月28日,天齐锂业公告,一季度实现营业收入114.49亿元,同比增长117.77%;净利润48.75亿元,同比增长46.49%;基本每股收益2.97元。
Write 5 bytes to file
New content: 4月28日hello,天齐锂业公告,一季度实现营业收入114.49亿元,同比增长117.77%;净利润48.75亿元,同比增长46.49%;基本每股收益2.97元。
此时文件example.txt中的内容如图10-4所示
图10-4 文件example.txt中的内容
10.3.3 接口io.Seeker
接口io.Seeker是 Go 语言标准库中的一个接口类型,它定义了一个用于查找和修改数据流中偏移量的方法。接口io.Seeker通过内置方法Seek()设置数据流中的偏移量。以下是接口io.Seeker的定义:
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
其中,方法Seek()中各个参数的说明如下:
- offset:相对于 whence 参数指定的位置的偏移量,可以为负数。
- whence:指定偏移量的基准位置,可以取值为 io.SeekStart(从数据流的开头开始计算)、io.SeekCurrent(从当前位置开始计算)或 io.SeekEnd(从数据流的结尾开始计算)。
- 返回值:有两个,新的偏移量和可能出现的错误。如果操作成功,则返回新的偏移量;否则,返回错误信息。
下面是一个使用接口io.Seeker的实例,功能是读取指定文件中的第二个字节并输出显示。
实例10-5:读取指定文件中的第二个字节并输出显示(源码路径:Go-codes\10\Seeker.go)
实例文件Seeker.go的具体实现代码如下所示。
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("example1.txt")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Seek(1, io.SeekStart) // 将偏移量设置为 1(即跳过第一个字节)
if err != nil {
panic(err)
}
buf := make([]byte, 1)
_, err = file.Read(buf)
if err != nil {
panic(err)
}
fmt.Printf("The second byte of the file is: %c\n", buf[0])
}
对上述代码的具体说明如下:
- 首先使用函数os.Open()打开一个名为 example.txt 的文件,并将其赋值给变量 file。
- 然后,使用方法file.Seek()将偏移量设置为 1,即跳过文件的第一个字节。
- 接着,使用方法file.Read()读取文件中的下一个字节,并将其输出到标准输出中。
假设 example.txt 文件包含以下内容:
Hello, world!
运行程序后会输出如下内容:
The second byte of the file is: e
之所以输出上述结果,是因为首先将偏移量设置为 1(即跳过第一个字符 H),然后从文件中读取下一个字节,即文件中的第二个字符 e,并将其输出到标准输出中。
注意:如果使用的是不同的文本文件或具有不同格式的文件,则输出结果可能与上述实例略有不同。
标签:10,读取,err,文件,接口,io From: https://blog.csdn.net/asd343442/article/details/139260491