您的位置:晶晶的博客>GoLang>golang:io和文件操作

golang:io和文件操作

文件系统

I/O:输入/输出Input/Output。io包抽象了输入(读)输出(写)原语。而文件操作也是一种io操作。

io抽象:io包

go语言的io包抽象可以看到一个自底向上逐一抽象的关于IO操作的实现。io包抽象了io相关操作的4个基础接口:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}

而这4个基础接口利用go语言结构体的组合特性,依次组合出:ReadWriterReadCloserWriteCloserReadWriteCloserReadSeekerReadSeekCloserWriteSeekerReadWriteSeeker 。当然io包还提供有其他诸如:ReaderFromWriterToReaderAtWriterAtByteReaderByteScannerByteWriterRuneReaderRuneScannerStringWriter等接口抽象。

看go语言的io包会发现除了定义了核心抽象接口外还提供了一些上述接口的实现结构体,还提供了一个ioutil子包,这种混乱是因为历史原因和go官方对go1兼容性的保证导致的,不过1.16开始官方把ioutil子包这个子包的绝大数功能迁移到了os包以及新增的fs包,更加的统一和层级分明。新项目应该尽量避免使用ioutil这个子包,使用os或fs包替代即可,查看1.16版的源码会发现很多如下代码:

// ReadFile reads the file named by filename and returns the contents.
// A successful call returns err == nil, not err == EOF. Because ReadFile
// reads the whole file, it does not treat an EOF from Read as an error
// to be reported.
//
// As of Go 1.16, this function simply calls os.ReadFile.
func ReadFile(filename string) ([]byte, error) {
	return os.ReadFile(filename)
}

官方提供的替换方法映射关系如下:

  • ioutil.Discard => io.Discard
  • ioutil.NopCloser => io.NopCloser
  • ioutil.ReadAll => io.ReadAll
  • ioutil.ReadDir => os.ReadDir (note: returns a slice of os.DirEntry rather than a slice of fs.FileInfo)
  • ioutil.ReadFile => os.ReadFile
  • ioutil.TempDir => os.MkdirTemp
  • ioutil.TempFile => os.CreateTemp
  • ioutil.WriteFile => os.WriteFile

文件系统:fs包

go1.16开始新增了一个文件系统的抽象接口io/fs包,用于抽象文件系统相关接口。这样也把原先的io、os两个包的边界做了一些隔离。与io包下定义的接口相类似,基础文件系统接口为fs.FS,以及通过组合扩展的glob文件协议的fs.GlobFS、遍历处理目录的fs.ReadDirFS和文件相关操作的fs.ReadFileFS,还有一个深的linux哲学的用于一些文件本身属性信息的fs.StatFS。同时还定义了fs.File这个接口用于操作文件和描述文件的fs.FileInfo,以及fs.DirEntry这个接口用于描述和操作目录。

fs.FS这个接口主要是为了与io包相关接口一样抽象统一文件相关操作。而具体的文件系统相关的操作方法则需要实现这些fs相关的接口。例如os包同时提供了一个os.DirFS方法返回的就是一个已经实现了fs.FS接口的文件系统。需要注意的是os包下也有一个os.File结构体,与fs包下通用抽象的fs.File接口有巨大差异。

文件操作

go语言文件操作相关放在os包里。

1、读文件

只读打开文件:func Open(name string) (*File, error) ,查看源码会发现调用的是OpenFile方法;

创建文件:func Create(name string) (file *File, err Error) ,创建的文件权限默认为0666,查看源码会发现调用的是OpenFile方法;若需要自定义权限则需使用OpenFile方法。

指定权限打开文件func OpenFile(name string, flag int, perm uint32) (file *File, err Error) ,这是一个核心基础方法,其中perm权限使用os包提供的常量虽然是一个十六进制数,多个权限取余,这就是linux哲学里掩码的强大为例。

根据文件描述符打开创建文件:func NewFile(fd uintptr, name string) *File ,这个方法比较底层,例如go对标准输出的抽象的变量os.Stdin就是通过这个方法创建的。

读取文件为比特数组:func ReadFile(name string) ([]byte, error) ,底层封装了Open方法,并处理了os.*File结构体直接返回文件内容的比特数组。

上述函数调用成功后,即可获得可以具体操作文件内容的os.*File结构体,这个结构体即可完成文件的具体读操作。

2、写文件

本质是操作os.*File结构体的方法,例如func (file *File) WriteString(s string) (ret int, err Error)func (file *File) WriteAt(b []byte, off int64) (n int, err Error) 等。

当然go也提供了快捷写文件的函数:func WriteFile(name string, data []byte, perm FileMode) error,底层调用OpenFile可写模式写入指定的比特数组。

3、重命名&删除文件

linux哲学里一切皆文件,我们传统认为的文件和目录都是操作文件。

文件重命名:func Rename(oldpath, newpath string) error

删除文件:func Remove(name string) error

当然文件操作还是有创建软链、创建目录等等许多方法。

---

参考资料:

① https://golang.google.cn/doc/go1compat

② https://github.com/polaris1119/The-Golang-Standard-Library-by-Example/blob/master/chapter01/01.1.md

③ https://golang.google.cn/doc/go1.16#ioutil

转载请注明本文标题和链接:《golang:io和文件操作
分享到:

相关推荐

哟嚯,本文评论功能关闭啦~