文件操作

2026年9月6日

文件 I/O 主要在 osiobufio。小文件一次读完,大文件按块或按行读。


文件读取

一次性读取

b, err := os.ReadFile("a.txt")
if err != nil {
	return err
}
fmt.Println(string(b))
go

整份进内存,配置、短文本够用。

获取当前 go 文件的路径

相对路径是相对进程工作目录,不是相对 .go 源文件。要用源文件旁边的数据文件,先拿到这个文件自己的路径:

_, file, _, _ := runtime.Caller(0)
dir := filepath.Dir(file)
p := filepath.Join(dir, "a.txt")
b, err := os.ReadFile(p)
go

编译进二进制之后,源码路径不一定还在,正式项目更常把文件放进 embed 或按工作目录约定。

分片读

自己准备一块 buffer,循环 Read,直到 io.EOF

f, err := os.Open("a.txt")
if err != nil {
	return err
}
defer f.Close()

buf := make([]byte, 1024)
for {
	n, err := f.Read(buf)
	if n > 0 {
		fmt.Print(string(buf[:n]))
	}
	if err == io.EOF {
		break
	}
	if err != nil {
		return err
	}
}
go

带缓冲读

bufio.Scanner 默认按行。大文件、日志常用:

sc := bufio.NewScanner(f)
for sc.Scan() {
	fmt.Println(sc.Text())
}
if err := sc.Err(); err != nil {
	return err
}
go

换分隔符:sc.Split(bufio.ScanWords),或自己写 SplitFunc。单行默认上限大约 64KB,超长行要加大 buffer。


文件写入

一次性写

err := os.WriteFile("b.txt", []byte("hello\n"), 0644)
go

存在就覆盖。

文件的打开方式

细控制用 os.OpenFile

标志含义
os.O_RDONLY只读
os.O_WRONLY只写
os.O_RDWR读写
os.O_CREATE不存在就创建
os.O_TRUNC打开时清空
os.O_APPEND追加
f, err := os.OpenFile("b.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
	return err
}
defer f.Close()
_, err = f.WriteString("一行\n")
go

完整模式就是这些标志按位或。只读打开已有文件用 os.Open

文件的权限

第三个参数是 Unix 权限,Windows 上基本忽略。三位八进制:所有者、组、其他人,每位是读(4)+写(2)+执行(1)。

含义
0644所有者读写,其他人只读
0755所有者可执行,其他人可读可执行
0600只有所有者读写

0644 就是 rw-r--r--


文件复制

src, err := os.Open("a.txt")
if err != nil {
	return err
}
defer src.Close()

dst, err := os.Create("b.txt")
if err != nil {
	return err
}
defer dst.Close()

_, err = io.Copy(dst, src)
go

os.Create 等于 O_RDWR|O_CREATE|O_TRUNC


目录操作

os.Mkdir("data", 0755)
os.MkdirAll("a/b/c", 0755) // 中间不存在也会建
entries, err := os.ReadDir(".")
for _, e := range entries {
	fmt.Println(e.Name(), e.IsDir())
}
os.Remove("b.txt")      // 文件或空目录
os.RemoveAll("a")       // 整棵删掉,小心
go

判断存在:os.Staterrors.Is(err, os.ErrNotExist)


参考文档