go 中的文件操作
文件操作是编程中非常常见的任务,Go 语言提供了丰富的标准库来处理文件操作。本篇文章将详细介绍 Go 语言中的文件操作,涵盖文件的创建、读取、写入、追加、删除、重命名、权限管理等内容,并通过代码示例帮助你轻松理解。
文件的创建与打开
创建文件
在 Go 中,可以使用 os.Create
函数创建一个新文件。如果文件已存在,os.Create
会清空文件内容。
package fileop
import (
"fmt"
"os"
)
func CreateFile(path string) error {
f, err := os.Create(path)
if err != nil {
fmt.Println("文件创建失败")
return err
}
defer f.Close()
fmt.Println("文件创建成功")
return nil
}
os.Create
会创建一个新文件,并返回一个*os.File
对象和error
。- 如果文件已存在,
os.Create
会将其截断为空文件。 - 使用
defer file.Close()
确保文件在操作完成后关闭。
打开文件
使用 os.Open
可以打开一个已存在的文件,但只能以只读模式打开。
package fileop
import (
"fmt"
"os"
)
func Open(path string) error {
f, err := os.Open(path)
if err != nil {
fmt.Println("文件打开失败")
return err
}
defer f.Close()
fmt.Println("文件打开成功")
return nil
}
os.Open
以只读模式打开文件。- 如果需要以其他模式(如写入或追加)打开文件,可以使用
os.OpenFile
。
使用 os.OpenFile
打开文件
os.OpenFile
提供了更灵活的文件打开方式,可以指定文件的打开模式和权限。
package main
import (
"fmt"
"os"
)
func OpenFileWithFlag(path string, flag int) error {
f, err := os.OpenFile(path, flag, 0666)
if err != nil {
fmt.Println("文件打开失败")
return err
}
defer f.Close()
fmt.Println("文件打开成功")
fmt.Println("文件打开方式:", f.Fd())
return nil
}
func TestOpenFileWithFlag(t *testing.T) {
flag := os.O_RDWR | os.O_CREATE
err := fileop.OpenFileWithFlag("test.txt", flag)
if err != nil {
t.Errorf("OpenFileWithFlag() error = %v", err)
}
t.Log("OpenFileWithFlag() success")
}
os.OpenFile
的三个参数分别是文件名、打开模式和文件权限。- 打开模式:
os.O_RDONLY
:只读os.O_WRONLY
:只写os.O_RDWR
:读写os.O_CREATE
:如果文件不存在则创建os.O_APPEND
:追加写入os.O_TRUNC
:清空文件- 等等…
- 文件权限使用 Unix 风格的权限位,例如
0644
表示文件所有者可读写,其他人只读。
如果你查看一下源码就会发现,实际上os.Open
背后就是调用的 os.Openfile
使用只读权限来打开文件。
文件打开模式
在计算机系统中,每个标志位实际上对应了一个二进制位。例如os.O_RDWR、os.O_CREATE
等每个常量都有一个特定的二进制表示,它们各自占据位的不同位置。通过或操作,可以将多个标志位合并到一个整数中,每个标志位在结果中仍然保持独立。当使用如os.O_RDWR | os.O_CREATE
这样的组合时,实际上是在设置一个二进制整数,其中的某些位被设置为1
,表示相应的模式被启用。在文件系统调用中,操作系统会解析这个整数,根据各个位的状态来决定以何种模式打开文件。
例如,os.O_RDWR
可能是二进制00000010
,而os.O_CREATE
可能是二进制00000100
。当它们进行或操作时,结果是00000110
,这个结果同时包含了读写和创建文件的功能。
因此,通过位或操作,Go 语言中的文件打开模式可以组合多个标志,从而实现灵活的文件操作。这种机制在许多编程语言和操作系统中都普遍存在,它提供了一种高效且简洁的方式来设置和组合多种选项。
文件的读取
读取整个小文件
使用 ioutil.ReadFile
可以一次性写入整个文件内容。从 Go 1.16 开始,推荐使用 os.ReadFile
替代 ioutil.ReadFile
。
package fileop
import (
"fmt"
"os"
)
func ReadFile(path string) ([]byte, error) {
b1, err := os.ReadFile(path)
if err != nil {
fmt.Println("文件读取失败")
return nil, err
}
fmt.Printf("文件读取成功, b1: %v\n", string(b1[:]))
return b1, nil
}
os.ReadFile
返回文件的字节切片,适合读取小文件。- 对于大文件,这种方式可能会导致内存占用过高。
逐行读取文件
使用bufio.Scanner
可以逐行读取文件内容,适合处理大文件。
func ReadFileWithBufIO(path string) error {
f, err := os.Open(path)
if err != nil {
fmt.Println("文件打开失败")
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Printf("读取文件失败,err %v\n", err.Error())
return err
}
return nil
}
bufio.Scanner
会逐行读取文件内容。scanner.Text()
返回当前行的字符串内容。
按字节读取文件
使用文件实例的Read
方法可以按字节读取文件内容。
func ReadFileWithBytes(path string) error {
b1, err := os.Open(path)
if err != nil {
fmt.Println("打开文件失败")
return err
}
defer b1.Close()
for {
buf := make([]byte, 5)
n, err := b1.Read(buf)
if err != nil {
if err.Error() == "EOF" {
break
}
fmt.Println(err.Error())
return err
}
fmt.Printf("读取到 %d 个字节成功,内容:%v\n", n, buf)
}
b1.Seek(0, 0)
for {
buf := make([]byte, 2)
n, err := b1.Read(buf)
if err != nil {
if err.Error() == "EOF" {
break
}
fmt.Println(err.Error())
return err
}
fmt.Printf("读取到 %d 个字节成功,内容:%v\n", n, buf)
}
return nil
}
file.Read
会读取文件内容到字节切片中,返回读取的字节数。- 适合需要精确控制读取字节数的场景,需要选择合适的 buf 大小。
- 读取过程中会造成指针的偏移,使用 Seek 方法可以重置指针的位置。
- 往 buf 缓冲中写入数据时,最后一次读取数据时需要注意手动处理,可能会读不满。
文件的写入
写入整个文件
使用 ioutil.WriteFile
可以一次性写入整个文件内容。从 Go 1.16 开始,推荐使用 os.WriteFile
替代 ioutil.WriteFile
。
package fileop
import (
"fmt"
"os"
)
func WriteFile(path string, data []byte) error {
err := os.WriteFile(path, data, os.ModePerm)
if err != nil {
fmt.Printf("err: %v\n", err)
return err
}
fmt.Printf("文件写入成功\n")
return nil
}
源码:
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
}
查看源码可以发现,WriteFile
方法实际是先使用OpenFile
函数来打开文件,打开模式为O_WRONLY|O_CREATE|O_TRUNC
,然后调用文件实例对象的Write
方法将内容写入文件。
追加写入文件
看过os.WriteFile
函数的源码后可知,文件写入实际就是调用文件对象实例的 Write 方法实现的,而如何写入,取决于使用 os.OpenFile
打开文件时的 flag,因此我们可以自行指定 flag os.O_APPEND
来实现追加写入。
package fileop
import (
"fmt"
"os"
)
func WriteFileWithAppend(path string) error {
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
fmt.Println("打开文件出错:", err)
return err
}
defer f.Close()
data := []byte("\n追加的内容")
_, err = f.Write(data)
if err != nil {
fmt.Println("写入文件出错:", err)
return err
}
fmt.Printf("追加写入成功\n")
return nil
}
os.O_APPEND
标志表示追加写入。file.Write
用于写入字节切片。
文件的删除与重命名
删除文件
使用 os.Remove
可以删除文件。
package fileop
import "os"
func RemoveFile(path string) error {
err := os.Remove(path)
if err != nil {
return err
}
return nil
}
os.Remove
用于删除文件或空目录。
重命名文件
使用 os.Rename
可以重命名文件或移动文件。
package fileop
import "os"
func RenameFile(oldPath, newPath string) error {
err := os.Rename(oldPath, newPath)
if err != nil {
return err
}
return nil
}
测试:
package fileop_test
import (
"testing"
"github.com/wuvikr/go-study/core/fileop"
)
func TestRenamefile(t *testing.T) {
err := fileop.RenameFile("test.txt", "tmp/test1.txt")
if err != nil {
t.Error(err)
}
}
os.Rename
可以重命名文件,也可以将文件移动到其他目录。
文件权限管理
修改文件权限
使用 os.Chmod
可以修改文件的权限。
package fileop
import "os"
func ChmodFile(path string, mode os.FileMode) error {
err := os.Chmod(path, mode)
if err != nil {
return err
}
return nil
}
os.Chmod
用于修改文件的权限,0755
表示文件所有者可读写执行,其他人可读执行。
获取文件信息
使用 os.Stat
可以获取文件的详细信息。
package fileop
import (
"fmt"
"os"
)
func StatFile(path string) error {
info, err := os.Stat(path)
if err != nil {
return err
}
fmt.Printf("文件信息: %v\n", info)
fmt.Printf("文件名称: %v\n", info.Name())
fmt.Printf("文件大小: %v\n", info.Size())
fmt.Printf("文件权限: %v\n", info.Mode())
fmt.Printf("文件修改时间: %v\n", info.ModTime())
fmt.Printf("文件是否为目录: %v\n", info.IsDir())
return nil
}
os.Stat
返回一个os.FileInfo
对象,包含文件的名称、大小、权限、修改时间等信息。
目录操作
创建目录
使用 os.Mkdir
可以创建单个目录。
package fileop
import (
"fmt"
"os"
)
func CreateDir(path string, mode os.FileMode) error {
if err := os.Mkdir(path, mode); err != nil {
fmt.Println("创建目录失败, err:", err)
return err
}
fmt.Println("创建目录成功")
return nil
}
创建多级目录
使用 os.MkdirAll
可以创建多级目录。
func CreateDirAll(path string, mode os.FileMode) error {
if err := os.MkdirAll(path, mode); err != nil {
fmt.Println("创建多级目录失败, err:", err)
return err
}
fmt.Println("创建多级目录成功")
return nil
}
os.MkdirAll
会递归创建所有不存在的目录。
总结
本文详细介绍了 Go 语言中的文件操作,包括文件的创建、读取、写入、追加、删除、重命名、权限管理以及目录操作。
掌握这些基础操作将为你后续的开发工作打下坚实的基础。希望本文对你有所帮助,Happy coding!
本作品采用《CC 协议》,转载必须注明作者和本文链接
我还以为能雕出什么花来呢,这样容易让老师傅怀疑人生的。