Go提供的文件处理标准库
os库
负责OS文件系统交互的具体实现。io库
读写 IO 的抽象层。fs库
文件系统的抽象层。
1、打开文件
Go中有两个函数用于打开文件:os.OpenFile和os.Open
1.1 os.OpenFile
func OpenFile(name string, flag int, perm FileMode) (*File, error)
参数说明:
-
name:文件名称
文件的名称,会项目
go.mod文件所在的路径下查找该文件。 -
flag:打开标志(打开模式)
控制文件的打开方式和行为。
const ( // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified. O_RDONLY int = syscall.O_RDONLY // open the file read-only. O_WRONLY int = syscall.O_WRONLY // open the file write-only. O_RDWR int = syscall.O_RDWR // open the file read-write. // The remaining values may be or'ed in to control behavior. O_APPEND int = syscall.O_APPEND // append data to the file when writing. O_CREATE int = syscall.O_CREAT // create a new file if none exists. O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist. O_SYNC int = syscall.O_SYNC // open for synchronous I/O. O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened. )标志 说明 os.O_RDONLY只读模式 os.O_WRONLY只写模式 os.O_RDWR读写模式 os.O_CREATE如果文件不存在则创建 os.O_APPEND追加模式(写入时添加到文件末尾) os.O_TRUNC打开时清空文件 os.O_EXCL与 O_CREATE 一起使用,文件必须不存在 可以组合使用,使用
|组合两个标志:os.O_RDONLY | os.O_WRONLY -
perm:文件权限
-
文件权限位解析
// 权限位分解: 0 6 6 6 │ │ │ └── 其他用户权限: 6 (读+写) │ │ └── 同组用户权限: 6 (读+写) │ └── 文件所有者权限: 6 (读+写) └── 特殊权限位: 0 (无特殊权限) // 权限数值含义: // 4 = 读权限 (r) // 2 = 写权限 (w) // 1 = 执行权限 (x) // 6 = 4+2 = 读 + 写 // 7 = 4+2+1 = 读 + 写 + 执行 -
文件权限位,使用 Unix 风格的权限表示
-
仅在
创建新文件时生效权限 说明 0644用户读写,组和其他只读 0755用户读写执行,组和其他读执行 0600仅用户读写 0666所有用户可读写
-
1.2 os.Open
func Open(name string) (*File, error)
底层是对os.OpenFile的再一次封装。
func Open(name string) (*File, error) {
//文件模式只读,perm=0表示没有设置任何权限位
return OpenFile(name, O_RDONLY, 0)
}
-
perm=0
表示没有设置任何权限位,在文件存在的时候,该perm参数会被忽略;但如果是创建文件,perm=0,创建的文件会表现为
不可读、不可写、不可执行。
1.3 其他相关函数
-
os.IsNotExist(err)
通过返回的错误判断是否文件不存在
func main() { file, err := os.Open("read1.md") defer file.Close() if os.IsNotExist(err) { fmt.Println("File does not exist") } if err != nil { fmt.Println(err) } fmt.Println(file.Name()) }
2、读取文件
文件的读取,*os.File类型提供了两个的函数:
// 将文件读进传入的字节切片
func (f *File) Read(b []byte) (n int, err error)
// 相较于第一种可以从指定偏移量读取
func (f *File) ReadAt(b []byte, off int64) (n int, err error)
2.1 Read(b []byte)读取文件
package main
import (
"errors"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("read.md")
defer file.Close()
if os.IsNotExist(err) {
fmt.Println("File does not exist")
return
}
if err != nil {
fmt.Println(err)
return
}
readFile, err := ReadFile(file)
fmt.Println(string(readFile))
}
// 读取文件
func ReadFile(file *os.File) ([]byte, error) {
readBuffer := make([]byte, 0, 0)
fmt.Println("初始长度:", len(readBuffer), "初始容量:", cap(readBuffer))
count := 0
for {
if len(readBuffer) == cap(readBuffer) {
count = count + 1
fmt.Println("扩容次数:", count)
// 扩容,当长度和容量一样,切片添加一个元素,Go底层会创建新的切片底层数组,并做扩容
tempBuffer := append(readBuffer, 0)
//添加一个元素后,需要将添加的元素去除,变成和原始的切片一样内容
readBuffer = tempBuffer[:len(readBuffer)]
}
// 继续读取文件
offset, err := file.Read(readBuffer[len(readBuffer):cap(readBuffer)])
fmt.Println("读取的内容:", string(readBuffer))
// 将已写入的数据归入切片
readBuffer = readBuffer[:len(readBuffer)+offset]
fmt.Println("归入后的内容:", string(readBuffer))
// 发生错误时
if err != nil {
if errors.Is(err, io.EOF) {
err = nil
}
return readBuffer, err
}
}
}
2.2 Go提供两个方便读取文件的函数
2.2.1 os.ReadFile
- func ReadFile(name string) ([]byte, error)
func main() {
file, err := os.ReadFile("read.md")
if err != nil {
panic(err)
}
fmt.Println(string(file))
}
2.2.2 io.ReadAll
- func ReadAll(r Reader) ([]byte, error)
func main() { openFile, err := os.Open("read.md") if err != nil { panic(err) } all, err := io.ReadAll(openFile) if err != nil { panic(err) } fmt.Println(string(all)) }
3、 写入文件内容
3.1 os.File结构体提供的写入函数
// 写入字节切片
func (f *File) Write(b []byte) (n int, err error)
// 写入字符串
func (f *File) WriteString(s string) (n int, err error)
// 从指定位置开始写,当以os.O_APPEND模式打开时,会返回错误
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
- 实例:以
os.O_APPEND写入文件,写入的数据会添加到文件尾部。func main() { file, err := os.OpenFile("read.md", os.O_WRONLY|os.O_TRUNC|os.O_CREATE|os.O_APPEND, 0666) if err != nil { panic(err) } defer file.Close() for i := range 10 { offset, err := file.WriteString("序号:" + strconv.FormatInt(int64(i), 10) + "\n") if err != nil { panic(err) } fmt.Println(offset) } readFile, err := os.ReadFile("read.md") if err != nil { panic(err) } fmt.Println(string(readFile)) }
3.2 Go提供两个方便写入文件的函数
3.2.1 os.WriteFile
- func WriteFile(name string, data []byte, perm FileMode) error
func main() {
err := os.WriteFile("hello.txt", []byte("Hello, World!\n"), 0777)
if err != nil {
panic(err)
}
fmt.Println("写入完成")
}
3.2.2 io.WriteString
- func WriteString(w Writer, s string) (n int, err error)
func main() { //os.O_WRONLY:可读可写 //os.O_CREATE:文件不存在时,创建 //os.O_APPEND:从文件尾部写入数据 //os.O_TRUNC:文件存在时,将数据截掉(即清空文件内容) file, err := os.OpenFile("openFile.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666) if err != nil { panic(err) } for i := range 10 { n, err := io.WriteString(file, "hello "+strconv.Itoa(i)+"\n") if err != nil { panic(err) } fmt.Println(n) } }
4、复制
文件的复制,需要同时打开两个文件。
Go中有三种方式实现文件的赋值:
4.1 将原文件读取,然后写入新的文件
func main() {
file, err := os.ReadFile("hello.txt")
if err != nil {
panic(err)
}
err = os.WriteFile("hello1.txt", file, 0666)
if err != nil {
panic(err)
}
}
4.2 使用os.File.ReadFrom
- func (f *File) ReadFrom(r io.Reader) (n int64, err error)
- 该复制方式,是将所有内容读取到内存中,然后在写到新的文件中去,在大数据量的文件中,该方式不适合,会造成内存容量不足。
func main() {
//只读打开原始文件
file, err := os.OpenFile("hello.txt", os.O_RDONLY, 0666)
if err != nil {
panic(err)
}
defer file.Close()
//打开新的只读文件
newFile, err := os.OpenFile("hello2.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
defer newFile.Close()
n, err := newFile.ReadFrom(file)
if err != nil {
panic(err)
}
fmt.Printf("%d bytes read\n", n)
}
4.3 使用io.Copy/io.CopyBuffer
- func Copy(dst Writer, src Reader) (written int64, err error)。
- io.CopyBuffer:可以指定缓冲区的大小。
- 一边读一边写,先将内容读到缓冲区中,再写入到目标文件中,缓冲区默认大小为 32KB。
func main() {
//只读打开原始文件
file, err := os.OpenFile("hello.txt", os.O_RDONLY, 0666)
if err != nil {
panic(err)
}
defer file.Close()
//打开新的只读文件
newFile, err := os.OpenFile("hello3.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
defer newFile.Close()
written, err := io.Copy(newFile, file)
if err != nil {
panic(err)
}
fmt.Printf("%d bytes written\n", written)
}
5、重命名
- os包下的rename函数
- func Rename(oldpath, newpath string) error
func main() {
err := os.Rename("hello3.txt", "copy.txt")
if err != nil {
panic(err)
}
fmt.Println("文件修改完成")
}
6、删除
-
删除单个文件或者空目录,当目录不为空时会返回错误
func Remove(name string) error -
删除指定目录的所有文件和目录包括子目录与子文件
func RemoveAll(path string) error
7、刷新
- 作用:将内存的中的数据落到磁盘中。
func main() {
create, err := os.Create("test.txt")
if err != nil {
panic(err)
}
defer create.Close()
_, err = create.Write([]byte("hello"))
if err != nil {
panic(err)
}
// 刷盘
if err := create.Sync();err != nil {
return
}
}
8、文件夹
8.1 文件夹读取
-
os.ReadDir-
func ReadDir(name string) ([]DirEntry, error)
func main() { // 当前目录 dir, err := os.ReadDir(".") if err != nil { fmt.Println(err) } else { for _, entry := range dir { fmt.Println(entry.Name()) } } }
-
-
*os.File.ReadDir- func (f *File) ReadDir(n int) ([]DirEntry, error) // n < 0时,则读取文件夹下所有的内容
func main() { // 当前目录 dir, err := os.Open(".") if err != nil { fmt.Println(err) } defer dir.Close() dirs, err := dir.ReadDir(-1) if err != nil { fmt.Println(err) } else { for _, entry := range dirs { fmt.Println(entry.Name()) } } }
- func (f *File) ReadDir(n int) ([]DirEntry, error) // n < 0时,则读取文件夹下所有的内容
8.2 文件夹创建
- func Mkdir(name string, perm FileMode) error // 用指定的权限创建指定名称的目录
- func MkdirAll(path string, perm FileMode) error // 相较于前者该函数会创建一切必要的父目录
- 实例
func main() { err := os.Mkdir("src", 0666) if err != nil { fmt.Println(err) } else { fmt.Println("创建成功") } }
8.3 文件夹复制
- 使用
filepath标准库
func CopyDir(src, dst string) error {
// 检查源文件夹的状态
_, err := os.Stat(src)
if err != nil {
return err
}
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
// 计算相对路径
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
// 拼接目标路径
destpath := filepath.Join(dst, rel)
// 创建文件夹
var dirpath string
var mode os.FileMode = 0755
if info.IsDir() {
dirpath = destpath
mode = info.Mode()
} else if info.Mode().IsRegular() {
dirpath = filepath.Dir(destpath)
}
if err := os.MkdirAll(dirpath, mode); err != nil {
return err
}
// 创建文件
if info.Mode().IsRegular() {
srcfile, err := os.Open(path)
if err != nil {
return err
}
// 一定要记得关闭文件
defer srcfile.Close()
destfile, err := os.OpenFile(destpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
if err != nil {
return err
}
defer destfile.Close()
// 复制文件内容
if _, err := io.Copy(destfile, srcfile); err != nil {
return err
}
return nil
}
return nil
})
}