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)
}
直接调用即可,相关办法已经屏蔽细节,但具体完成咱们仍是有必要了解的。