Skip to content
mermaid
flowchart TD
    A["<b>os</b><br>核心:创建、读写、删除、权限"]
    
    B["<b>io</b><br>通用 Reader/Writer 接口"]
    C["<b>bufio</b><br>缓冲读写,性能优化"]
    D["<b>filepath</b><br>跨平台路径处理"]
    E["<b>ioutil</b> ⚠️<br>Go 1.16 已弃用 → os / io"]
    
    A --> B
    A --> C
    A --> D
    A -.-> E
    
    classDef osBox fill:#00BCD4,stroke:#00BCD4,color:#fff,stroke-width:0px;
    classDef ioBox fill:#fff,stroke:#4CAF50,stroke-width:2px;
    classDef bufioBox fill:#fff,stroke:#9C27B0,stroke-width:2px;
    classDef filepathBox fill:#fff,stroke:#FF9800,stroke-width:2px;
    classDef ioutilBox fill:#fff,stroke:#F44336,stroke-width:2px,stroke-dasharray: 5 5;
    
    class A osBox;
    class B ioBox;
    class C bufioBox;
    class D filepathBox;
    class E ioutilBox;
mermaid
flowchart LR
    F["os.Open()"] --> G["bufio.Scanner"] --> H["处理数据"] --> I["defer Close()"]
    
    style F fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px
    style G fill:#d5e8d4,stroke:#82b366,stroke-width:2px
    style H fill:#ffe6cc,stroke:#d79b00,stroke-width:2px
    style I fill:#f8cecc,stroke:#b85450,stroke-width:2px

注意:ioutil 包在 Go 1.16 已弃用,其功能已迁移到 os(如 os.ReadFileos.WriteFile)和 io(如 io.ReadAll)。新代码应直接使用 osio

创建文件

os.Create 创建一个新文件。如果文件已存在,会被截断(清空内容)。返回的文件对象必须关闭以释放系统资源

go
func main() {
	file, err := os.Create("test.txt")
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close() // defer 确保函数结束时关闭文件
}

文件打开和关闭

函数模式说明
os.Open(name)只读最简单的方式,只读打开
os.Create(name)读写 + 创建/截断创建新文件或清空已有文件
os.OpenFile(name, flag, perm)自定义可指定读写、追加、创建等标志

Open

go
// 只读打开
file, err := os.Open("test.txt")
if err != nil {
    fmt.Println("打开失败:", err)
    return
}
defer file.Close()
fmt.Println("文件打开成功")

OpenFile

go
f, err := os.OpenFile("test.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
    fmt.Println("打开失败:", err)
    return
}
defer f.Close()
f.WriteString("我是一行数据\n")

参数:

  1. 要操作的文件
  2. 通过位运算得出的标志信息(每一个常量占据了独一无二的位置)
  3. 文件权限(linux/unix),0表示八进制
标志说明
os.O_RDONLY只读(默认)
os.O_WRONLY只写
os.O_RDWR读写
os.O_APPEND追加模式,写入内容追加到文件末尾
os.O_CREATE文件不存在时创建
os.O_TRUNC打开时清空文件内容
os.O_EXCL与 CREATE 配合,文件已存在时报错

文件读取

文件有三种读取方式:

  • 一次性读取(os.ReadFile("file.txt")
  • 逐行读取(bufio.NewScanner(file)
  • 流式读取(io.ReadAll(reader)

一次性读取

通过 os.ReadFile("file.txt") 一次性读取文件内容,能够自动打开、读取、关闭文件。适合处理小文件

go
func main() {
	content, err := os.ReadFile("test.txt")
	if err != nil {
		fmt.Println("读取失败:", err)
		return
	}
	fmt.Println(string(content))
}

逐行读取

适用于大型文件

go
func main() {
	// 打开一份文件
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("打开文件失败:", err)
	}
	defer file.Close()
	// 逐行读取文件内容
	sc := bufio.NewScanner(file)
	for sc.Scan() {
		fmt.Println(sc.Text())
	}
	// 打印扫描器的错误信息
	if err := sc.Err(); err != nil {
		fmt.Println("扫描文件失败:", err)
	}
}

注意:bufio.Scanner 默认最大行长度为 64KB。如果文件中有超长行,需要在调用 Scan() 前设置 scanner.Buffer(make([]byte, 0), maxSize) 来增大缓冲区

流式读取

适用于网络响应、已打开的文件

go
func main() {
	file, err := os.Open("test.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// 从 Reader 读取所有数据
	data, err := io.ReadAll(file)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}

文件写入

一次性写入

使用os.WriteFile 将数据一次性写入文件,但是会覆盖文件原有内容

go
func main() {
	err := os.WriteFile("test.txt", []byte("你好,世界!"), 0644)
	if err != nil {
		log.Fatal(err)
	}
}

分次写入

使用 File 对象进行分次写入

go
func main() {
	file, err := os.Create("test.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// 方式1:写入字符串
	file.WriteString("字符串\n")

	// 方式2:写入字节切片
	data := []byte("字节切片\n")
	file.Write(data)

	// 方式3:格式化写入
	fmt.Fprintf(file, "格式化写入: %d + %d = %d\n", 1, 2, 3)
}

缓冲写入

bufio.Writer 会先将数据写入内存缓冲区,攒够后再批量写入磁盘,减少 I/O 次数

go
func main() {
	file, err := os.Create("test.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// 创建带缓冲的写入器
	writer := bufio.NewWriter(file)

	// 写入多行(先进入缓冲区,不会立即写磁盘)
	for i := 0; i < 1000; i++ {
		writer.WriteString("这是第")
		writer.WriteString(fmt.Sprintf("%d", i))
		writer.WriteString("行\n")
	}

	// Flush 将缓冲区剩余数据写入文件
	if err := writer.Flush(); err != nil {
		log.Fatal("刷新缓冲区失败:", err)
	}
	log.Println("缓冲写入完成")
}

注意:在结束前务必调用 Flush()

追加写入

go
func main() {
	// O_APPEND: 追加模式
	// O_CREATE: 不存在则创建
	// O_WRONLY: 只写
	file, err := os.OpenFile("test.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// 追加,不会覆盖
	file.WriteString("今天天气真不错\n")
}

删除

go
func main() {
	err := os.Remove("temp.txt")
	if err != nil {
		log.Fatal("删除失败:", err)
	}
	log.Println("删除成功")
}

重命名

go
func main() {
	// 重命名 / 移动文件
	err := os.Rename("old.txt", "new.txt")
	if err != nil {
		log.Fatal("重命名失败:", err)
	}
	log.Println("重命名成功")
}

跟Linux中的 mv 命令差不多

文件信息

查看文件的详细信息

go
func main() {
	info, err := os.Stat("test.txt")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("文件名:    ", info.Name())
	fmt.Println("大小:      ", info.Size(), "字节")
	fmt.Println("权限:      ", info.Mode())
	fmt.Println("修改时间:  ", info.ModTime())
	fmt.Println("是目录吗:  ", info.IsDir())
}

查看文件是否存在:go判断文件是否存是使用 os.Stat() 函数配合 os.IsNotExist() 错误判断来实现的

go
// 判断文件/文件夹是否存在
func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil // 文件(或目录)存在
	}
	if os.IsNotExist(err) {
		return false, nil // 文件(或目录)不存在
	}
	return false, err // 发生了其他错误(比如权限不足、路径语法错误等)
}

func main() {
	exists, err := fileExists("test.txt")
	if err != nil {
		fmt.Printf("判断时发生了其他错误: %v\n", err)
		return
	}
	if exists {
		fmt.Println("文件存在")
	} else {
		fmt.Println("文件不存在")
	}
}

目录

go
func main() {
	// 创建单个目录
	err := os.Mkdir("新目录", 0755)
	if err != nil {
		log.Fatal(err)
	}

	// 递归创建多级目录(父目录不存在也会自动创建)
	err = os.MkdirAll("新目录/新目录2/新目录3", 0755)
	if err != nil {
		log.Fatal(err)
	}

	// 读取目录内容
	entries, err := os.ReadDir(".")
	if err != nil {
		log.Fatal(err)
	}
	for _, entry := range entries {
		mark := "📄" // 文件
		if entry.IsDir() {
			mark = "📁" // 目录
		}
		fmt.Printf("%s %s\n", mark, entry.Name())
	}

	// 删除空目录
	os.Remove("新目录")

	// 递归删除目录及其所有内容
	os.RemoveAll("新目录")
}

操作

文件复制

通过 io.Copy 在两个文件之间复制

go
// copyFile 复制文件的通用函数
func copyFile(source, destination string) (int64, error) {
	srcFile, err := os.Open(source)
	if err != nil {
		return 0, err
	}
	defer srcFile.Close()

	dstFile, err := os.Create(destination)
	if err != nil {
		return 0, err
	}
	defer dstFile.Close()

	// io.Copy 自动管理缓冲区,高效复制
	return io.Copy(dstFile, srcFile)
}

临时文件/目录

go
func main() {
	// 创建临时文件(自动命名,前缀为 "app-")
	tmpFile, err := os.CreateTemp("", "app-*.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer os.Remove(tmpFile.Name()) // 用完后清理
	fmt.Println("临时文件:", tmpFile.Name())

	// 创建临时目录
	tmpDir, err := os.MkdirTemp("", "app-*")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(tmpDir) // 用完后清理
	fmt.Println("临时目录:", tmpDir)
}

递归遍历

通过 filepath.Walk 可以递归遍历目录树中的所有文件和子目录

go
func main() {
	root := "." // 从当前目录开始遍历

	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 打印路径和大小
		size := ""
		if !info.IsDir() {
			size = fmt.Sprintf(" (%d bytes)", info.Size())
		}
		fmt.Println(path + size)
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}
}