跳到主要内容
Go 语言文件操作实战:读写、压缩与目录管理 | 极客日志
Go / Golang
Go 语言文件操作实战:读写、压缩与目录管理 本文详解 Go 语言中 os、bufio、io 等包的文件操作方法。涵盖文件读取(缓存、分片、寻址)、写入(追加、缓冲)、拷贝、统计信息获取、目录遍历及压缩解压(zip、tar.gz)。通过优化后的代码示例,展示现代 Go 标准库的最佳实践,包括错误处理与资源释放,帮助开发者高效处理本地文件系统任务。
Go 文件操作实战指南
在 Go 语言开发中,文件系统操作是基础且高频的需求。本文将结合标准库 os、bufio、io 等包,梳理从读取、写入到压缩的完整流程。代码示例基于现代 Go 版本(1.16+),推荐使用 os 替代已废弃的 ioutil。
1. 文件读取
Go 提供了多种读取方式,根据文件大小和场景选择合适的方法。
1.1 带缓冲读取
适用于大文件或需要逐行处理的场景。使用 bufio.Reader 可以减少系统调用次数。
func main () {
file, err := os.Open("./abc.txt" )
if err != nil {
fmt.Printf("Open file error: %v\n" , err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n' )
if err == io.EOF {
break
}
if err != nil {
fmt.Println("Read error:" , err)
break
}
fmt.Print(line)
}
}
1.2 一次性读取
适合配置文件等小文件,直接返回字节切片。
func main () {
bs, err := os.ReadFile("./abc.txt" )
if err != nil {
fmt.Printf("Read file error: %v\n" , err)
return
}
fmt.Printf("%s\n" , bs)
}
1.3 分片读取
手动控制缓冲区大小,适合对内存敏感的场景。
func main () {
file, err := os.Open("abc.txt" )
if err != {
(err)
}
file.Close()
{
buf := ([] , )
n, err := file.Read(buf)
n > {
fmt.Printf( , buf[:n])
}
err == io.EOF {
}
err != {
(err)
}
}
}
nil
panic
defer
for
make
byte
32
if
0
"%s"
if
break
if
nil
panic
1.4 文件寻址 使用 Seek 方法移动文件指针,支持相对当前位置或绝对位置跳转。
func main () {
file, _ := os.Open("abc.txt" )
defer file.Close()
pos, _ := file.Seek(5 , io.SeekStart)
fmt.Println("Position:" , pos)
pos, _ = file.Seek(-2 , io.SeekCurrent)
fmt.Println("Position:" , pos)
}
1.5 分隔符读取 利用 Scanner 按单词或自定义规则分割内容。
func main () {
file, _ := os.Open("abc.txt" )
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
panic (err)
}
}
1.6 按行读取 比 ReadString 更高效,自动处理换行符。
func main () {
file, err := os.Open("abc.txt" )
if err != nil {
panic (err)
}
defer file.Close()
reader := bufio.NewReader(file)
for {
line, _, err := reader.ReadLine()
if err == io.EOF {
break
}
if err != nil {
panic (err)
}
fmt.Printf("%s\n" , line)
}
}
2. 文件写入 写入模式决定了数据如何被保存,常见有覆盖、追加等。
2.1 常规写入 使用 OpenFile 指定标志位,灵活控制行为。
func writeFile () {
file, err := os.OpenFile("abc.txt" , os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644 )
if err != nil {
panic (err)
}
defer file.Close()
_, err = file.Write([]byte ("hello world!" ))
if err != nil {
panic (err)
}
fmt.Println("Wrote bytes" )
}
2.2 快速写入 func main () {
err := os.WriteFile("abc.txt" , []byte ("add a new line" ), 0644 )
if err != nil {
panic (err)
}
}
2.3 缓冲写入 批量写入时减少 IO 开销,记得最后调用 Flush。
func bufferedWrite () {
file, err := os.OpenFile("abc.txt" , os.O_WRONLY, 0644 )
if err != nil {
panic (err)
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString("Buffered string\n" )
writer.Flush()
}
3. 文件拷贝 小文件可直接读取后写入,大文件建议使用 io.Copy。
3.1 直接拷贝 func main () {
bs, err := os.ReadFile("src.txt" )
if err != nil {
panic (err)
}
err = os.WriteFile("dst.txt" , bs, 0600 )
if err != nil {
panic (err)
}
}
3.2 缓冲拷贝 func CopyFile (dstFileName, srcFileName string ) (written int64 , err error ) {
srcFile, err := os.Open(srcFileName)
if err != nil {
return 0 , err
}
defer srcFile.Close()
dstFile, err := os.OpenFile(dstFileName, os.O_CREATE|os.O_WRONLY, 0600 )
if err != nil {
return 0 , err
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
4. 文件信息与统计
4.1 文件信息 func statFile () {
info, err := os.Stat("abc.txt" )
if err != nil {
if os.IsNotExist(err) {
fmt.Println("file not exist" )
return
}
panic (err)
}
fmt.Println("Name:" , info.Name())
fmt.Println("Size:" , info.Size())
fmt.Println("Mode:" , info.Mode())
fmt.Println("IsDir:" , info.IsDir())
}
4.2 字符统计 type Statistic struct {
Char int
Number int
Space int
Other int
}
func main () {
file, err := os.Open("./abc.txt" )
if err != nil {
fmt.Printf("Open file error: %v\n" , err)
return
}
defer file.Close()
stat := Statistic{}
reader := bufio.NewReader(file)
for {
bs, err := reader.ReadString('\n' )
if err == io.EOF {
break
}
for _, c := range []rune (bs) {
switch {
case c >= 'a' && c <= 'z' , c >= 'A' && c <= 'Z' :
stat.Char++
case c >= '0' && c <= '9' :
stat.Number++
case c == ' ' || c == '\t' :
stat.Space++
default :
stat.Other++
}
}
}
fmt.Printf("%v\n" , stat)
}
5. 目录遍历 func main () {
err := filepath.Walk("/tmp" , func (path string , info fs.FileInfo, err error ) error {
if err != nil {
return err
}
fmt.Println(path)
return nil
})
if err != nil {
panic (err)
}
}
6. 压缩文件
6.1 Zip 压缩与解压
func CompressZip (rootDir string , writer io.Writer) error {
zipWriter := zip.NewWriter(writer)
defer zipWriter.Close()
return filepath.Walk(rootDir, func (path string , info os.FileInfo, err error ) error {
if err != nil {
return err
}
header, _ := zip.FileInfoHeader(info)
header.Name = strings.TrimPrefix(path, filepath.Dir(rootDir))
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
fileWriter, _ := zipWriter.CreateHeader(header)
if !info.Mode().IsRegular() {
return nil
}
reader, _ := os.Open(path)
defer reader.Close()
_, err = io.Copy(fileWriter, reader)
return err
})
}
func ExtractZip (zipFilename string ) error {
baseDir := filepath.Dir(zipFilename)
zipReader, err := zip.OpenReader(zipFilename)
if err != nil {
return err
}
defer zipReader.Close()
for _, file := range zipReader.File {
zippedFile, err := file.Open()
if err != nil {
return err
}
defer zippedFile.Close()
extractedFilePath := filepath.Join(baseDir, file.Name)
if file.FileInfo().IsDir() {
os.MkdirAll(extractedFilePath, file.Mode())
} else {
outFile, err := os.OpenFile(extractedFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, file.Mode())
if err != nil {
return err
}
io.Copy(outFile, zippedFile)
outFile.Close()
}
}
return nil
}
6.2 Tar & Gzip func CompressTarGz (rootDir string , writer io.Writer) error {
gzipWriter := gzip.NewWriter(writer)
defer gzipWriter.Close()
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
return filepath.Walk(rootDir, func (path string , info os.FileInfo, err error ) error {
if err != nil {
return err
}
var link string
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
link, _ = os.Readlink(path)
}
header, _ := tar.FileInfoHeader(info, link)
header.Name = strings.TrimPrefix(path, filepath.Dir(rootDir))
tarWriter.WriteHeader(header)
if !info.Mode().IsRegular() {
return nil
}
reader, _ := os.Open(path)
defer reader.Close()
io.Copy(tarWriter, reader)
return nil
})
}
func ExtractTarGz (reader io.Reader, baseDir string ) error {
gzipReader, err := gzip.NewReader(reader)
if err != nil {
return err
}
defer gzipReader.Close()
tarReader := tar.NewReader(gzipReader)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
filename := filepath.Join(baseDir, header.Name)
switch header.Typeflag {
case tar.TypeDir:
os.MkdirAll(filename, fs.FileMode(header.Mode))
case tar.TypeReg:
outFile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fs.FileMode(header.Mode))
if err != nil {
return err
}
io.Copy(outFile, tarReader)
outFile.Close()
}
}
return nil
}
7. 临时文件 func main () {
tmpDir, err := os.MkdirTemp("" , "temp" )
if err != nil {
panic (err)
}
defer os.RemoveAll(tmpDir)
tmpFile, err := os.CreateTemp(tmpDir, "tmp.txt" )
if err != nil {
panic (err)
}
defer tmpFile.Close()
tmpFile.Write([]byte ("test" ))
}
8. 其他常用操作 func main () {
err := os.Truncate("abc.txt" , 100 )
err = os.Rename("old.txt" , "new.txt" )
err = os.Remove("abc.txt" )
err = os.Chmod("abc.txt" , 0700 )
err = os.Chtimes("abc.txt" , time.Now(), time.Now())
err = os.Symlink("abc.txt" , "abc_sym.txt" )
}
以上涵盖了 Go 文件操作的核心 API。实际开发中请根据业务需求选择合适的方案,并注意错误处理和资源释放。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online