文件读写基础 #

文件操作是程序与外部世界交互的重要方式。Go语言提供了多种文件读写方法,从简单便捷的函数到精细控制的操作。

读取整个文件 #

使用 os.ReadFile #

最简单的读取文件方式是使用 os.ReadFile,它一次性读取整个文件内容:

go
package main

import (
    "fmt"
    "os"
)

func main() {
    content, err := os.ReadFile("example.txt")
    if err != nil {
        fmt.Println("读取文件失败:", err)
        return
    }
    
    fmt.Println("文件内容:")
    fmt.Println(string(content))
}

os.ReadFile 返回字节切片 []byte,需要转换为字符串显示。

使用 io/ioutil 包 #

在Go 1.16之前,ioutil.ReadFile 是常用方法:

go
package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    content, err := ioutil.ReadFile("example.txt")
    if err != nil {
        fmt.Println("读取文件失败:", err)
        return
    }
    
    fmt.Println(string(content))
}

注意: 从Go 1.16开始,ioutil.ReadFile 已被废弃,建议使用 os.ReadFile

写入文件 #

使用 os.WriteFile #

写入文件最简单的方法是使用 os.WriteFile

go
package main

import (
    "fmt"
    "os"
)

func main() {
    content := []byte("Hello, Go文件写入!\n这是第二行内容。")
    
    err := os.WriteFile("output.txt", content, 0644)
    if err != nil {
        fmt.Println("写入文件失败:", err)
        return
    }
    
    fmt.Println("文件写入成功!")
}

文件权限 0644 表示:

  • 所有者:读写权限 (6 = 4+2)
  • 组:读权限 (4)
  • 其他:读权限 (4)

打开文件进行读写 #

使用 os.Open 只读打开 #

go
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("打开文件失败:", err)
        return
    }
    defer file.Close()
    
    buf := make([]byte, 1024)
    n, err := file.Read(buf)
    if err != nil {
        fmt.Println("读取失败:", err)
        return
    }
    
    fmt.Printf("读取了 %d 字节\n", n)
    fmt.Println(string(buf[:n]))
}

使用 os.OpenFile 自定义模式 #

os.OpenFile 提供更灵活的文件打开方式:

go
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.OpenFile("example.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        fmt.Println("打开文件失败:", err)
        return
    }
    defer file.Close()
    
    _, err = file.WriteString("\n追加的新内容")
    if err != nil {
        fmt.Println("写入失败:", err)
        return
    }
    
    fmt.Println("内容追加成功!")
}

常用文件打开标志:

标志 说明
os.O_RDONLY 只读模式
os.O_WRONLY 只写模式
os.O_RDWR 读写模式
os.O_CREATE 文件不存在则创建
os.O_APPEND 追加模式
os.O_TRUNC 打开时清空文件
os.O_EXCL 与O_CREATE一起使用,文件必须不存在

使用 bufio 进行缓冲读写 #

缓冲读写可以提高I/O性能,特别适合处理大文件:

缓冲读取 #

go
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("打开文件失败:", err)
        return
    }
    defer file.Close()
    
    scanner := bufio.NewScanner(file)
    lineNum := 1
    
    for scanner.Scan() {
        fmt.Printf("第%d行: %s\n", lineNum, scanner.Text())
        lineNum++
    }
    
    if err := scanner.Err(); err != nil {
        fmt.Println("读取错误:", err)
    }
}

缓冲写入 #

go
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("output.txt")
    if err != nil {
        fmt.Println("创建文件失败:", err)
        return
    }
    defer file.Close()
    
    writer := bufio.NewWriter(file)
    
    for i := 1; i <= 5; i++ {
        _, err := writer.WriteString(fmt.Sprintf("第%d行内容\n", i))
        if err != nil {
            fmt.Println("写入失败:", err)
            return
        }
    }
    
    writer.Flush()
    fmt.Println("写入完成!")
}

重要: 使用 bufio.Writer 时,必须调用 Flush() 方法确保所有数据写入文件。

使用 io.Copy 复制文件 #

go
package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    src, err := os.Open("source.txt")
    if err != nil {
        fmt.Println("打开源文件失败:", err)
        return
    }
    defer src.Close()
    
    dst, err := os.Create("destination.txt")
    if err != nil {
        fmt.Println("创建目标文件失败:", err)
        return
    }
    defer dst.Close()
    
    copied, err := io.Copy(dst, src)
    if err != nil {
        fmt.Println("复制失败:", err)
        return
    }
    
    fmt.Printf("复制了 %d 字节\n", copied)
}

读取特定位置 #

使用 Seek 方法可以定位文件读写位置:

go
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("打开文件失败:", err)
        return
    }
    defer file.Close()
    
    offset, err := file.Seek(10, 0)
    if err != nil {
        fmt.Println("定位失败:", err)
        return
    }
    fmt.Printf("当前位置: %d\n", offset)
    
    buf := make([]byte, 20)
    n, _ := file.Read(buf)
    fmt.Println(string(buf[:n]))
}

Seek 的第二个参数表示起始位置:

  • 0 - 文件开头
  • 1 - 当前位置
  • 2 - 文件末尾

实用示例:统计文件行数 #

go
package main

import (
    "bufio"
    "fmt"
    "os"
)

func countLines(filename string) (int, error) {
    file, err := os.Open(filename)
    if err != nil {
        return 0, err
    }
    defer file.Close()
    
    scanner := bufio.NewScanner(file)
    count := 0
    
    for scanner.Scan() {
        count++
    }
    
    return count, scanner.Err()
}

func main() {
    count, err := countLines("example.txt")
    if err != nil {
        fmt.Println("错误:", err)
        return
    }
    
    fmt.Printf("文件共有 %d 行\n", count)
}

小结 #

方法 适用场景 特点
os.ReadFile 小文件读取 简单便捷,一次性读取
os.WriteFile 小文件写入 简单便捷,覆盖写入
os.Open 只读操作 需要手动管理
os.OpenFile 复杂文件操作 灵活控制打开模式
bufio 大文件处理 缓冲提高性能
io.Copy 文件复制 高效复制

选择合适的文件读写方法取决于具体需求。小文件可以使用便捷函数,大文件推荐使用缓冲读写。