前言
哈喽,大家好,我是
asong
。咱们都晓得在
Unix
中万物都被称为文件,文件解决是一个十分常见的问题,所以本文就总结了Go
语言操作文件的常见形式,整体思路如下:
Go语言版本:1.18
本文所有代码曾经上传github
:https://github.com/asong2020/...
操作文件包含哪些
操作一个文件离不开这几个动作:
- 创立文件
- 关上文件
- 读取文件
- 写入文件
- 敞开文件
- 打包/解包
- 压缩/解压缩
- 扭转文件权限
- 删除文件
- 挪动文件
- 重命名文件
- 清空文件
所以本文就针对这些操作总结了一些示例办法供大家参考;
Go语言操作文件可应用的库
Go语言官网库:os
、io/ioutil
、bufio
涵盖了文件操作的所有场景,
os
提供了对文件IO
间接调用的办法,bufio
提供缓冲区操作文件的办法,io/ioutil
也提供对文件IO
间接调用的办法,不过Go语言在Go1.16
版本曾经弃用了io/ioutil
库,这个io/ioutil
包是一个定义不明确且难以了解的货色汇合。该软件包提供的所有性能都已移至其余软件包,所以io/ioutil
中操作文件的办法都在io
库有雷同含意的办法,大家当前在应用到ioutil中的办法是能够通过正文在其余包找到对应的办法。
文件的根底操作
这里我把 创立文件、关上文件、敞开文件、扭转文件权限这些归为对文件的基本操作,对文件的基本操作间接应用os
库中的办法即可,因为咱们须要进行IO
操作,来看上面的例子:
import ( "log" "os")func main() { // 创立文件 f, err := os.Create("asong.txt") if err != nil{ log.Fatalf("create file failed err=%s\n", err) } // 获取文件信息 fileInfo, err := f.Stat() if err != nil{ log.Fatalf("get file info failed err=%s\n", err) } log.Printf("File Name is %s\n", fileInfo.Name()) log.Printf("File Permissions is %s\n", fileInfo.Mode()) log.Printf("File ModTime is %s\n", fileInfo.ModTime()) // 扭转文件权限 err = f.Chmod(0777) if err != nil{ log.Fatalf("chmod file failed err=%s\n", err) } // 扭转拥有者 err = f.Chown(os.Getuid(), os.Getgid()) if err != nil{ log.Fatalf("chown file failed err=%s\n", err) } // 再次获取文件信息 验证扭转是否正确 fileInfo, err = f.Stat() if err != nil{ log.Fatalf("get file info second failed err=%s\n", err) } log.Printf("File change Permissions is %s\n", fileInfo.Mode()) // 敞开文件 err = f.Close() if err != nil{ log.Fatalf("close file failed err=%s\n", err) } // 删除文件 err = os.Remove("asong.txt") if err != nil{ log.Fatalf("remove file failed err=%s\n", err) }}
写文件
快写文件
os
/ioutil
包都提供了WriteFile
办法能够疾速解决创立/关上文件/写数据/敞开文件,应用示例如下:
func writeAll(filename string) error { err := os.WriteFile("asong.txt", []byte("Hi asong\n"), 0666) if err != nil { return err } return nil}
按行写文件
os
、buffo
写数据都没有提供按行写入的办法,所以咱们能够在调用os.WriteString
、bufio.WriteString
办法是在数据中退出换行符即可,来看示例:
import ( "bufio" "log" "os")// 间接操作IOfunc writeLine(filename string) error { data := []string{ "asong", "test", "123", } f, err := os.OpenFile(filename, os.O_WRONLY, 0666) if err != nil{ return err } for _, line := range data{ _,err := f.WriteString(line + "\n") if err != nil{ return err } } f.Close() return nil}// 应用缓存区写入func writeLine2(filename string) error { file, err := os.OpenFile(filename, os.O_WRONLY, 0666) if err != nil { return err } // 为这个文件创建buffered writer bufferedWriter := bufio.NewWriter(file) for i:=0; i < 2; i++{ // 写字符串到buffer bytesWritten, err := bufferedWriter.WriteString( "asong真帅\n", ) if err != nil { return err } log.Printf("Bytes written: %d\n", bytesWritten) } // 写内存buffer到硬盘 err = bufferedWriter.Flush() if err != nil{ return err } file.Close() return nil}
偏移量写入
某些场景咱们想依据给定的偏移量写入数据,能够应用os
中的writeAt
办法,例子如下:
import "os"func writeAt(filename string) error { data := []byte{ 0x41, // A 0x73, // s 0x20, // space 0x20, // space 0x67, // g } f, err := os.OpenFile(filename, os.O_WRONLY, 0666) if err != nil{ return err } _, err = f.Write(data) if err != nil{ return err } replaceSplace := []byte{ 0x6F, // o 0x6E, // n } _, err = f.WriteAt(replaceSplace, 2) if err != nil{ return err } f.Close() return nil}
缓存区写入
os
库中的办法对文件都是间接的IO
操作,频繁的IO
操作会减少CPU
的中断频率,所以咱们能够应用内存缓存区来缩小IO
操作,在写字节到硬盘前应用内存缓存,当内存缓存区的容量达到肯定数值时在写内存数据buffer到硬盘,bufio
就是这样示一个库,来个例子咱们看一下怎么应用:
import ( "bufio" "log" "os")func writeBuffer(filename string) error { file, err := os.OpenFile(filename, os.O_WRONLY, 0666) if err != nil { return err } // 为这个文件创建buffered writer bufferedWriter := bufio.NewWriter(file) // 写字符串到buffer bytesWritten, err := bufferedWriter.WriteString( "asong真帅\n", ) if err != nil { return err } log.Printf("Bytes written: %d\n", bytesWritten) // 查看缓存中的字节数 unflushedBufferSize := bufferedWriter.Buffered() log.Printf("Bytes buffered: %d\n", unflushedBufferSize) // 还有多少字节可用(未应用的缓存大小) bytesAvailable := bufferedWriter.Available() if err != nil { return err } log.Printf("Available buffer: %d\n", bytesAvailable) // 写内存buffer到硬盘 err = bufferedWriter.Flush() if err != nil{ return err } file.Close() return nil}
读文件
读取全文件
有两种形式咱们能够读取全文件:
os
、io/ioutil
中提供了readFile
办法能够疾速读取全文io/ioutil
中提供了ReadAll
办法在关上文件句柄后能够读取全文;
import ( "io/ioutil" "log" "os")func readAll(filename string) error { data, err := os.ReadFile(filename) if err != nil { return err } log.Printf("read %s content is %s", filename, data) return nil}func ReadAll2(filename string) error { file, err := os.Open("asong.txt") if err != nil { return err } content, err := ioutil.ReadAll(file) log.Printf("read %s content is %s\n", filename, content) file.Close() return nil}
逐行读取
os
库中提供了Read
办法是依照字节长度读取,如果咱们想要按行读取文件须要配合bufio
一起应用,bufio
中提供了三种办法ReadLine
、ReadBytes("\n")
、ReadString("\n")
能够按行读取数据,上面我应用ReadBytes("\n")
来写个例子:
func readLine(filename string) error { file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { return err } bufferedReader := bufio.NewReader(file) for { // ReadLine is a low-level line-reading primitive. Most callers should use // ReadBytes('\n') or ReadString('\n') instead or use a Scanner. lineBytes, err := bufferedReader.ReadBytes('\n') bufferedReader.ReadLine() line := strings.TrimSpace(string(lineBytes)) if err != nil && err != io.EOF { return err } if err == io.EOF { break } log.Printf("readline %s every line data is %s\n", filename, line) } file.Close() return nil}
按块读取文件
有些场景咱们想依照字节长度读取文件,这时咱们能够如下办法:
os
库的Read
办法os
库配合bufio.NewReader
调用Read
办法os
库配合io
库的ReadFull
、ReadAtLeast
办法
// use bufio.NewReaderfunc readByte(filename string) error { file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { return err } // 创立 Reader r := bufio.NewReader(file) // 每次读取 2 个字节 buf := make([]byte, 2) for { n, err := r.Read(buf) if err != nil && err != io.EOF { return err } if n == 0 { break } log.Printf("writeByte %s every read 2 byte is %s\n", filename, string(buf[:n])) } file.Close() return nil}// use osfunc readByte2(filename string) error{ file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { return err } // 每次读取 2 个字节 buf := make([]byte, 2) for { n, err := file.Read(buf) if err != nil && err != io.EOF { return err } if n == 0 { break } log.Printf("writeByte %s every read 2 byte is %s\n", filename, string(buf[:n])) } file.Close() return nil}// use os and io.ReadAtLeastfunc readByte3(filename string) error{ file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { return err } // 每次读取 2 个字节 buf := make([]byte, 2) for { n, err := io.ReadAtLeast(file, buf, 0) if err != nil && err != io.EOF { return err } if n == 0 { break } log.Printf("writeByte %s every read 2 byte is %s\n", filename, string(buf[:n])) } file.Close() return nil}
分隔符读取
bufio
包中提供了Scanner
扫描器模块,它的次要作用是把数据流宰割成一个个标记并除去它们之间的空格,他反对咱们定制Split
函数做为分隔函数,分隔符能够不是一个简略的字节或者字符,咱们能够自定义分隔函数,在分隔函数实现分隔规定以及指针挪动多少,返回什么数据,如果没有定制Split
函数,那么就会应用默认ScanLines
作为分隔函数,也就是应用换行作为分隔符,bufio
中还提供了默认办法ScanRunes
、ScanWrods
,上面咱们用SacnWrods
办法写个例子,获取用空格分隔的文本:
func readScanner(filename string) error { file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { return err } scanner := bufio.NewScanner(file) // 能够定制Split函数做分隔函数 // ScanWords 是scanner自带的分隔函数用来找空格分隔的文本字 scanner.Split(bufio.ScanWords) for { success := scanner.Scan() if success == false { // 呈现谬误或者EOF是返回Error err = scanner.Err() if err == nil { log.Println("Scan completed and reached EOF") break } else { return err } } // 失去数据,Bytes() 或者 Text() log.Printf("readScanner get data is %s", scanner.Text()) } file.Close() return nil}
打包/解包
Go语言的archive
包中提供了tar
、zip
两种打包/解包办法,这里以zip
的打包/解包为例子:
zip
解包示例:
import ( "archive/zip" "fmt" "io" "log" "os")func main() { // Open a zip archive for reading. r, err := zip.OpenReader("asong.zip") if err != nil { log.Fatal(err) } defer r.Close() // Iterate through the files in the archive, // printing some of their contents. for _, f := range r.File { fmt.Printf("Contents of %s:\n", f.Name) rc, err := f.Open() if err != nil { log.Fatal(err) } _, err = io.CopyN(os.Stdout, rc, 68) if err != nil { log.Fatal(err) } rc.Close() }}
zip
打包示例:
func writerZip() { // Create archive zipPath := "out.zip" zipFile, err := os.Create(zipPath) if err != nil { log.Fatal(err) } // Create a new zip archive. w := zip.NewWriter(zipFile) // Add some files to the archive. var files = []struct { Name, Body string }{ {"asong.txt", "This archive contains some text files."}, {"todo.txt", "Get animal handling licence.\nWrite more examples."}, } for _, file := range files { f, err := w.Create(file.Name) if err != nil { log.Fatal(err) } _, err = f.Write([]byte(file.Body)) if err != nil { log.Fatal(err) } } // Make sure to check the error on Close. err = w.Close() if err != nil { log.Fatal(err) }}
总结
本文归根结底是介绍os
、io
、bufio
这些包如何操作文件,因为Go
语言操作提供了太多了办法,借着本文全都介绍进去,在应用的时候能够很不便的当作文档查问,如果你问用什么办法操作文件是最优的办法,这个我也没法答复你,须要依据具体场景剖析的,如果这些办法你都晓得了,在写一个benchmark比照一下就能够了,实际才是测验真谛的唯一标准。
本文所有代码曾经上传github
:https://github.com/asong2020/...
好啦,本文到这里就完结了,我是asong,咱们下期见。
欢送关注公众号:Golang梦工厂