go的io与文件
背景
整理文件与io的关系,
go中各种读取文件的方式的对比.
介绍
文件在打开之前,可以看作一个与os交互的对象,
可以创建,删除,移动,设置权限等.
文件打开时,也是一个与os交互的对象.
根据文件的权限,读取的方式等等,写的方式等等,
对文件的操作会有不同的效果和反馈.
而文件打开后就变成一个I/O的reader或writer,
单纯一些,无非就是
- 读到指定buffer中
- 返回一个buffer
- 读取到字符串
亦或是
- 读整个文件
- 按行
文件和文件夹的常用操作
1 | // 以创建文件为例 |
文件的读写权限
OpenFile
1 | file, err := os.OpenFile("1.txt", 读写模式, 权限) |
文件打开时的常见错误:
- no such file or directory
- permission denied
文件指针
- 成功时返回指针
- 失败返回nil
读写模式
- O~RDONLY~ 只读(与只写冲突时优先只写)
- O~WRONLY~ 只写(会从开头开始写)
- O~APPEND~ 附加到尾部(相当于只写的扩展版,没有写权限时不生效)
- O~RDWR~ 读写(一个比较常用的选项,默认会在尾部附加内容)
- O~CREATE~ 不存在时将创建新的文件
- O~EXCL~ 与O~CREATE配合~,文件已经存在时报错
- O~SYNC~ 同步IO
- O~TRUNC~ 如果可能,打开时清空文件
读写模式可以使用 |
分隔,组合使用.
权限
权限通常只会在有 os.O_CREATE
时被使用.
文件权限在go看来是一个8进制的数字,因为最大到7,写法上比如 0755
.
os包提供了许多关键字,其中只有 os.ModePerm
与文件有关,值等效于 0777
,
其他的比如设备权限 os.ModeDevice
为 D---------
.
因此如果创建文本文件,通常手动写 0644
会是一个合适的选择.
注意即使权限是0777,也会创建出0755的文件,因为受到系统的限制.
使用 umask -S
查看系统设置的权限上限, 使用 umask
可以得到 umask -S
的掩码.
Open
是一个快捷方式,底层为
1 | OpenFile(name, O_RDONLY, 0) |
io包
io包提供了对IO操作的最基本接口,基本考虑了操作IO时的各种情况.
包含了4大类方法和接口,这些类别中的每种接口都为不同的需求服务.互不相关
比如Reader和ReaderAt完全没有关系.
有些接口比如Reader,或使用函数(比如使用MultiReader),
或转为类型(比如转为LimitedReader),能够完成更多的功能.
- 读
- Reader(Read)
- LimitedReader(Read)
- PipeReader(Read, Close, CloseWithError)
- TeeReader(Read)
- MulltiReader(Read)
- ReaderAt(ReadAt)
- SectionReader(Size, Read, ReadAt, Seek)
- ByteReader(ReadByte)
- ByteScanner(ReadByte, UnreadByte)
- RuneReader(ReadRune)
- RuneScanner(ReadRune, UnreadRune)
- ReaderFrom(ReadFrom)
- Reader(Read)
- 写
- Writer(Write)
- PipeWriter(Write, Close, CloseWithError)
- MultiWriter(Write)
- WriterAt(WriteAt)
- ByteWriter(WriteByte)
- WriterTo(WriteTo)
- Writer(Write)
- 关
- Closer(Close)
- 找
- Seeker(Seek)
有时这四类接口还会相互组合,产生新的接口,从名字就能看出是哪些基本接口组合成的
- ReadCloser
- ReadSeeker
- WriteCloser
- WriteSeeker
- ReadWriteCloser
- ReadWriteSeeker
此外还有一些方便操作的函数
- ReadAtLeast(reader, buf, min) (n, err), 要求最少读取min字节,文件内容太短,buffer太小,都会报错
- ReadFull(reader, buf) (n, err), 要求正好填满buffer,文件内容太短会报错
- Copy(writer, reader) (n64, err) 从reader拷贝数据到writer
- CopyN(writer, reader, n) (n64, err) 从reader拷贝n字节数据到writer,数量不够会报错
- WriteString(writer, str) (n, err) 向writer写入字符串的是快捷方式
否则就是 n, err = writer.Write([]byte(str))
os包中的File
File对象实现了一部分io包中的方法.
- Read
- ReadAt
- Write
- WriteAt
- WriteString
- Seek
- Close
诸如ReadByte等方法,需要借助其他包来对reader进行转换才能操作.
另外os包还提供简便的读写文件内容的方法
- os.ReadFile(filename)
- os.WriteFile(filename, data, 权限), 默认读写模式是
O_WRONLY|O_CREATE|O_TRUNC
1 | func main() { |
ioutil包
主要用于解决一些io操作中的麻烦.
- Read函数需要自定义一个固定大小的buffer,然后函数返回n,最后使用
buffer[:n]
来获取有效信息.
ioutil.ReadAll(reader)
方法返回一个合适大小,装有内容的buffer. - 先打开文件再读取文件内容很麻烦.
ioutil.Readfile(filename)
一步到位(事实上仅仅是用了os.ReadFile(filename)
) - 先打开文件在写入内容也很麻烦.
ioutil.WriteFile(filename, data, permission)
一步到位(事实上也是用了os包的内容)
1 | func main() { |
bufio包
主要功能
- 将io包中的各种接口,统一到几个结构体上.
- Reader
拥有Read,ReadByte,ReadRune等等方法. - Writer
拥有Write,WriteByte,WriteString等等方法 - ReadWriter
- Scanner
(Reader的分化,强调文本与文本之间的分隔,比如换行和空格在[]byte看来就是数字,不确定意义,而Scanner看来无意义)
拥有Split,Scan,Bytes,Text,Err等方法
- Reader
- 为这些结构体带上多种内部字段,以及特殊的函数,用于各种目的
- buffer用于临时保存数据
- err用于存放错误
- 读写位置的offset
- Writer的Flush方法专门用于将数据写入io
可见简单操作文件可以使用ioutil,
而要实现对文件的丰富操作,离不开bufio包.
1 | func main() { |
Writer同理,暂略