Go语言文件操作

文件操作是大多数程序的基本需求。Go标准库提供了丰富的文件处理功能,主要分布在os、io、bufio、ioutil(Go 1.16后部分功能移至os和io)等包中。本章将介绍文件的创建、打开、读写、复制、删除以及路径操作等常见任务。

注意:Go 1.16开始,ioutil包已被弃用,推荐使用os和io包中的对应函数。

1. 打开与创建文件

使用os.Open以只读方式打开;os.Create创建或截断文件;os.OpenFile提供更精细的控制。

打开文件的几种方式
package main

import (
    "fmt"
    "os"
)

func main() {
    // 1. 只读方式打开
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file.Close()

    // 2. 创建或截断文件(如果存在则覆盖)
    file2, err := os.Create("newfile.txt")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    defer file2.Close()

    // 3. 以指定模式打开(读写、追加、创建等)
    file3, err := os.OpenFile("test.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file3.Close()
}

常用标志:os.O_RDONLY(只读)、os.O_WRONLY(只写)、os.O_RDWR(读写)、os.O_APPEND(追加)、os.O_CREATE(不存在则创建)、os.O_TRUNC(清空文件)。

2. 写入文件

2.1 使用 os.WriteString 或 File.Write

file, err := os.Create("output.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()
// 写入字符串
_, err = file.WriteString("Hello, Go!\n")
// 写入字节切片
data := []byte("Another line\n")
_, err = file.Write(data)

2.2 使用 bufio 缓冲写入(高效)

file, _ := os.Create("buffered.txt")
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString("缓冲写入第一行\n")
writer.WriteString("第二行\n")
writer.Flush() // 确保数据写入磁盘

2.3 使用 os.WriteFile(一次性写入全部内容)

content := []byte("一次性写入的内容")
err := os.WriteFile("one_shot.txt", content, 0644)

3. 读取文件

3.1 按字节读取

file, _ := os.Open("data.txt")
defer file.Close()
buf := make([]byte, 1024)
n, err := file.Read(buf)
fmt.Printf("读取了 %d 字节\n", n)

3.2 使用 bufio 按行读取

逐行读取文件
file, err := os.Open("lines.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
    line := scanner.Text()
    fmt.Println(line)
}
if err := scanner.Err(); err != nil {
    log.Fatal(err)
}

3.3 一次性读取全部内容(小文件)

data, err := os.ReadFile("small.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

4. 追加内容到文件

file, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
    log.Fatal(err)
}
defer file.Close()
if _, err := file.WriteString("新追加的日志行\n"); err != nil {
    log.Fatal(err)
}

5. 复制文件

使用io.Copy实现高效复制。

func copyFile(src, dst string) error {
    source, err := os.Open(src)
    if err != nil {
        return err
    }
    defer source.Close()

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

    _, err = io.Copy(destination, source)
    return err
}

6. 重命名与删除

// 重命名(移动)
err := os.Rename("old.txt", "new.txt")
if err != nil {
    log.Fatal(err)
}

// 删除文件
err := os.Remove("unneeded.txt")
if err != nil {
    log.Fatal(err)
}

// 删除目录及其内容
err := os.RemoveAll("temp_dir")

7. 检查文件是否存在

通过os.Stat并判断错误类型。

func fileExists(path string) bool {
    _, err := os.Stat(path)
    return !os.IsNotExist(err)
}

// 使用示例
if fileExists("config.json") {
    fmt.Println("配置文件存在")
} else {
    fmt.Println("配置文件不存在")
}

8. 获取文件信息

info, err := os.Stat("example.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())

9. 目录操作

9.1 创建目录

// 创建单个目录(父目录必须存在)
err := os.Mkdir("mydir", 0755)

// 创建多级目录
err := os.MkdirAll("path/to/deep/dir", 0755)

9.2 读取目录内容

entries, err := os.ReadDir(".")
if err != nil {
    log.Fatal(err)
}
for _, entry := range entries {
    fmt.Println(entry.Name(), entry.IsDir())
}

10. 临时文件

// 创建临时文件(自动生成随机后缀)
tmpFile, err := os.CreateTemp("", "prefix-*.txt")
if err != nil {
    log.Fatal(err)
}
defer os.Remove(tmpFile.Name()) // 清理
tmpFile.WriteString("临时数据")
tmpFile.Close()

// 创建临时目录
tmpDir, err := os.MkdirTemp("", "testdir-*")
defer os.RemoveAll(tmpDir)

综合示例:简单的日志写入器

追加写入并自动创建日志文件
package main

import (
    "fmt"
    "log"
    "os"
    "time"
)

type Logger struct {
    file *os.File
}

func NewLogger(filename string) (*Logger, error) {
    file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return nil, err
    }
    return &Logger{file: file}, nil
}

func (l *Logger) Write(message string) {
    timestamp := time.Now().Format("2006-01-02 15:04:05")
    line := fmt.Sprintf("[%s] %s\n", timestamp, message)
    if _, err := l.file.WriteString(line); err != nil {
        log.Println("写入日志失败:", err)
    }
}

func (l *Logger) Close() {
    l.file.Close()
}

func main() {
    logger, err := NewLogger("app.log")
    if err != nil {
        log.Fatal(err)
    }
    defer logger.Close()
    logger.Write("程序启动")
    logger.Write("执行某项操作")
    logger.Write("程序正常退出")
    fmt.Println("日志写入完成,查看 app.log")
}
小结:Go语言的文件操作API设计简洁且功能完整。掌握os、bufio、io等包的核心函数,足以应对绝大多数的文件处理需求。注意错误处理和资源释放(defer close)。下一章我们将学习Go语言的测试与基准测试。