golang 通过 os 包进行文件读写
go
中 os包
主要与操作系统打交道,实际底层还是通过相关的系统调用实现文件的读写操作,今天我们就来聊聊通过 os包
实现文件的读写操作。
我们在使用 os包
进行相关操作时,主要流程:
- 读操作
open -> read -> close
- 写操作
open -> read -> write -> close
总体来说,读写操作都躲不过上述流程,现在我们先看看 os包
的相关接口。
1.os包的源码
Open()/OpenFile()/Create()
// Open func Open(name string) (*File, error) { return OpenFile(name, O_RDONLY, 0) // 默认通过 OpenFile实现,只读方式 } // Create func Create(name string) (*File, error) { return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) // 实际也是调用OpenFIle,注意flag中声明了 create操作 } // OpenFile,注释中已明确,主要就是,大部分时候,我们应该明确使用的读-open,新建-create,文件追加-openfile(声明append flag) func OpenFile(name string, flag int, perm FileMode) (*File, error) { testlog.Open(name) f, err := openFileNolog(name, flag, perm) if err != nil { return nil, err } f.appendMode = flag&O_APPEND != 0 return f, nil }
os.File
这个结构体很重要,几乎所有的文件操作都是以此结构体作为操作对象,但我们今天主要关心文件读写,我们只看读写相关方法实现。
看看读写的源码:
// Read // Read reads up to len(b) bytes from the File and stores them in b. // It returns the number of bytes read and any error encountered. // At end of file, Read returns 0, io.EOF. func (f *File) Read(b []byte) (n int, err error) { if err := f.checkValid("read"); err != nil { return 0, err } n, e := f.read(b) return n, f.wrapErr("read", e) } // read // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. func (f *File) read(b []byte) (n int, err error) { n, err = f.pfd.Read(b) runtime.KeepAlive(f) return n, err } // Write // Write writes len(b) bytes from b to the File. // It returns the number of bytes written and an error, if any. // Write returns a non-nil error when n != len(b). func (f *File) Write(b []byte) (n int, err error) { if err := f.checkValid("write"); err != nil { return 0, err } n, e := f.write(b) if n < 0 { n = 0 } if n != len(b) { err = io.ErrShortWrite } epipecheck(f, e) if e != nil { err = f.wrapErr("write", e) } return n, err } // write // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. func (f *File) write(b []byte) (n int, err error) { n, err = f.pfd.Write(b) runtime.KeepAlive(f) return n, err }
简单概括下就是:
Read
,从File
对象中读取内容,装进[]byte
中Write
,把[]byte
中的内容,写入File
对象中
2.通过 os 实现文件读写
读操作
var filename string = "D:\\demo1\\src\\demo23\\go-io\\file\\file.txt" func read() []byte { file, err := os.Open(filename) defer file.Close() if err != nil { fmt.Printf("err: %s\n", err) return nil } var content []byte buf := make([]byte, 10) var r int for { n, err := file.Read(buf) // 每次读取10字节内容 content = append(content, buf[:n]...) // 读取内容写入返回数组中 r += n if err != nil { if err == io.EOF { fmt.Printf("err: %s\n", err) } break } } return content }
写操作
var dstFile string = "os_file_w.txt" func write() bool { fw, err := os.OpenFile(dstFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 777) // 使用 OpenFile,传入 flag,可以实现安全的打开创建写入 if err != nil { fmt.Printf("err: %s\n", err) return false } defer fw.Close() b, err := os.ReadFile(filename) // 读取源文件内容 if err != nil { fmt.Printf("err: %s\n", err) return false } _, err = fw.Write(b) // 调用 File。Write() 方法实现数据的写入,实际场景中,要考虑写入文件大小 if err != nil { fmt.Printf("err: %s\n", err) return false } return true }
通过上面的文件操作,我们可以看到,通过 os包
File
对象,我们可以简单实现文件的读和写。
3.通过 os.ReadFile/os.WriteFile 实现文件读写
在介绍 io包
时我们提到了相关函数,这里我们深入看看。
先看看源码:
// ReadFile reads the named file 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. func ReadFile(name string) ([]byte, error) { f, err := Open(name) if err != nil { return nil, err } defer f.Close() var size int if info, err := f.Stat(); err == nil { size64 := info.Size() if int64(int(size64)) == size64 { size = int(size64) } } size++ // one byte for final read at EOF // If a file claims a small size, read at least 512 bytes. // In particular, files in Linux's /proc claim size 0 but // then do not work right if read in small pieces, // so an initial read of 1 byte would not work correctly. if size < 512 { size = 512 } data := make([]byte, 0, size) for { if len(data) >= cap(data) { d := append(data[:cap(data)], 0) data = d[:len(data)] } n, err := f.Read(data[len(data):cap(data)]) data = data[:len(data)+n] if err != nil { if err == io.EOF { err = nil } return data, err } } } // WriteFile writes data to the named file, creating it if necessary. // If the file does not exist, WriteFile creates it with permissions perm (before umask); // otherwise WriteFile truncates it before writing, without changing permissions. func WriteFile(name string, data []byte, perm FileMode) error { f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm) if err != nil { return err } _, err = f.Write(data) if err1 := f.Close(); err1 != nil && err == nil { err = err1 } return err }
从上面的源码中,我们可以清晰地看到,实际上也是将 File
的相关方法做了一层封装,进而实现读写操作,比如在读操作中,在循环读取内容时,需要先判断 data
切片是否需要扩容,然后再读取内容到 data
中,直到碰到 io.EOF error
,或者是其他 error
,而对于写操作,则在打开文件后,直接调用 File.Write()
写入数据,总体来说,在这个层面的封装比较简单。
最后我们看看通过 os.ReadFile/os.WriteFile
的使用示例:
var dstFile_os string = "os_wr_file_w.txt" func readFile(filename string) ([]byte, error) { return os.ReadFile(filename) } func writeFile(dst string, data []byte) error { return os.WriteFile(dst, data, 777) }
直接调用即可,相关方法已经屏蔽细节,但具体实现我们还是有必要了解的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
2021-04-25 3w字详解java集合
2021-04-25 ELK实时日志分析平台环境部署--完整记录(转)
2021-04-25 ELK简介(转)