导图社区 go语言IO
这是一篇关于go语言IO的思维导图,主要内容包括:IO标准库拓扑,文件系统,IO操作。IO操作本质就是Input和Output,输入和输出,读取和写入。
编辑于2025-03-03 01:40:58go语言IO
IO操作
IO操作本质就是Input和Output,输入和输出,读取和写入
Reader 读取
TeeReader
主要用来分流和重复读取流中的内容
实现:里面有一个Reader和一个Writer,通过Reader的read方法读取出内容后,使用Writer的write方法把byte序列重新写入流中
核心接口
Read
Read(p []byte) (n int,err error)
每次读取会使流中的偏移位置进行更改,简单来说只能读取一次
读取的序列长度可以小于数组的长度
将Reader流中的内容,读取到byte序列中
ReaderAt
ReadAt([]byte,offset int) (n int,err error)
从Reader流中指定偏移量位置开始读取内容到byte序列中
每次读取不会改变流中的偏移位置,简单来说可以重复读取
每次读取的流长度必须等于数组长度,不然会报异常
支持并发读取
Writer
Write(p []byte) (n int,err error)
将byte序列写入流中
写入会改变流当前的偏移量
WriterAt
WriteAt(p []byte,offset int)
从流指定的偏移位置开始写入
写入并不会改变流当前的偏移量
Seeker
Seek(offset int)
修改当前流的偏移位置到指定位置
Closer
Closer() error
关闭IO资源,如文件或者网络网络连接
高级Reader和Writer
TeeReader
由一个Reader和一个Writer组成
实现了Read()方法,从当前Reader中读取内容到byte序列中,并将内容写入Writer流中,实现了数据流复制车了两份
reader := strings.NewReader("Hello, TeeReader!") // 创建多个 Writer var buf1, buf2, buf3 bytes.Buffer // 链式调用 TeeReader teeReader := io.TeeReader(reader, &buf1) // 第一个 TeeReader teeReader = io.TeeReader(teeReader, &buf2) // 第二个 TeeReader teeReader = io.TeeReader(teeReader, &buf3) // 第三个 TeeReader // 读取数据 io.ReadAll(teeReader) 调用解释 teeReader := io.TeeReader(reader, &buf1) // 第一个 TeeReader 此时 Reader是reader,Writer 是buf1 teeReader = io.TeeReader(teeReader, &buf2) // 第二个 TeeReader 此时 Reader是上一个teeReader,Writer是buf2 teeReader = io.TeeReader(teeReader, &buf3) // 第三个 TeeReader 此时Reader是第二teeReader ,Writer是buf3 调用第三个teeReader的read方法 调用第二个teeReader的read方法,并把内容写入buf3 调用第一个teeReader的read方法,并把内容写入buf2 调用Reader的read方法,并把内容写入buf1 于是reader中的内容成功复制到3个buf中了,完成了reader流的复制和分流
LimiterReader
在流式的数据处理中,EOF通常用来标识数据流的结束,而LimitReader则允许用户显式的指定数据流的长度,当读取一定长度的数据量后,LimitReader会主动返回EOF,防止数据溢出
MultiReader
可以将多个Reader组合在一起,然后顺序进行read调用,这样用户就不需在多个Reader之间切换就能连续读取数据
MultiWriter
和MultiReader类似,可以将多个Writer组合在一起,然后可以byte序列循环写入多个Writer流,实现了数据流的复制
SectionReader
可以对数据流的特定部分进行读取,是对ReaderAt接口的封装,同时还需要实现Seeker接口。可以很方便的重复去读特定区域的内容
PipeReader和PipeWriter
需要配合使用,由io.Pipe()创建,实现了同步传输的内存数据流管理,可用于不同Goruntine进行数据交换,适用于生产者--消费组模式
通用IO函数
GO语言中的IO操作都被抽象为流式数据处理,得益于标准库中封装的各种Copy和Read函数,都是基于Reader和Writer进行实现的
Copy函数
Copy、CopyN、CopyBuffer这些函数负责将数据流从Reader传输到Writer,底层都是基于copyBuffer进行处理,包括处理了IO错误,内部循环条件、EOF检测等
copyBuffer
使用指定的缓冲区、通过一个大的for循环、每循环一次就调用一次Read和Write方法,直到所有数据被处理完毕或者遇到异常
CopyN
Copy函数是处理的无边界的数据流,CopyN提供了数据流的限制功能
Read封装函数
ReadFull、ReadAtLeast、ReadAll,这些专注与Reader的操作,提供了完全读取、指定读取、全部读取等全部的逻辑
ReadAll函数
持续的读取数据直到EOF或者异常状态,读到EOF并不会报错,err仍然等于nil,关注的是整个数据流的读取状态
ReadFull函数
目的是读取足够多的数据到缓冲区,如果缓冲区没有读满的情况则会报错,如果在缓冲区未读满的时候遇到EOF则会报错UnexpectedEOF错误,关注的是缓冲区的读满情况
ReadAtLeast函数
用于确保至少读取了知道量的数据,是ReadFull的底层实现,如果读取的数据没有到达指定的最小值、则会返回报错,如果在每达到最小值是遇到EOF,则会报错UnexpectedEOF错误,关注的是读取数据量的大小
文件系统
GO语言通过os包中File类型的抽象操作来实现对系统文件的操作,存放在os包中
ReadrFile
通过传入文件路径,可以直接把文件读取内容,不需要进行open、read、close等操作
实现方式:
调用open打开文件
创建byte数组,循环读取文件内容,byte长度不够进行扩容,直到读取到EOF或者其他异常
close关闭文件
WriteFile
一次性将内存中的数据写入指定文件,同样也不需要进行open、read、close等操作
实现
调用open打开文件
调用write函数将内容写入文件
close关闭文件
ReadDir
读取指定目录下的所有文件列表
注意以上方法需要注意文件内容大小,避免内存溢出的情况
文件系统
go1.16版本前Open操作返回的是具体类型,会导致与程序与os.file类耦合紧密
FS接口
通过io/fs为文件系统提供抽象定义,使得Go中的文件系统抽象和操作系统的文件系统得以解耦
基础接口
Open(name string) (File,error)
返回fs.File文件接口,代表一个文件
fs.File文件接口定义
Stat()(FileInfo,error)
Read([]byte)(int,error)
Close() error
一个文件系统的文件至少要提供以上三个方法的实现
fs.FileInfo的接口定义
Name() string 文件名称
Size() int64 文件大小
Mode() FileMode 打开模式
ModTime time.Time 修改时间
IsDir() 是否是目录
Sys() 扩展接口,返回操作系统相关的文件结构
以上接口构成了一个简单的只读文件系统,至少要有一个Open方法,并返回一个可读文件,我们可以在此基础上扩展FS接口来扩展文件系统的功能
扩展文件系统
ReaderDirFS
新增读取目录功能
GlobFS
根据路径通配符全局查找文件功能
StatFS
根据路径查询文件信息
ReadFileFS
根据路径查询文件内容
文件系统实现
Dir文件系统
embed文件系统
IO标准库拓扑
字节IO
通过bytes类可以很方便的把字节序列转成为流进行读取或者写入
bytes.NewReader(b []byte)可以把字节序列变成流
bytes,NewWriter(b []byte)可以把字节数组变成写入流
字符串IO
字符串也可以转为成Reader,进行流的各种读取操作
由于字符串是不可以变类型,所以无法变成写入流
网络IO
网络IO与文件IO较为复杂,单独开一节进行讲解
文件IO
网络IO与文件IO较为复杂,单独开一节进行讲解
缓冲IO
在存储系统中,相比于内存中的一次复制或者计算操作,一次磁盘的IO操作代价要大得多,处于性能优化得考虑,合并IO操作较少不必要得系统调用显得尤为重要
bufio包中提供了缓冲IO的高效实现,通过一个带缓冲区的Writer,后续写入的数据并不是直接写入底层,而是先存储在内存缓冲区当中,当缓冲区满了再统一写入底层,可以减少实际的IO操作次数