文件操作

33.1 文件操作基础

Go 语言提供了丰富的文件操作功能,主要通过 osio 包实现。

打开文件

package main

import (
    "fmt"
    "os"
)

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

    fmt.Println("文件打开成功")
}

打开文件(指定模式)

package main

import (
    "fmt"
    "os"
)

func main() {
    // 打开文件,指定权限
    file, err := os.OpenFile("test.txt", os.O_RDWR|os.O_CREATE, 0644)
    if err != nil {
        fmt.Println("打开文件失败:", err)
        return
    }
    defer file.Close()

    fmt.Println("文件打开成功")
}

文件打开模式

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

文件权限

0644: 所有者读写,组和其他用户只读
0755: 所有者读写执行,组和其他用户读执行
0600: 所有者读写

33.2 读取文件

读取整个文件

package main

import (
    "fmt"
    "os"
)

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

    // 方法2:使用 io.ReadAll
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file.Close()

    content2, err := io.ReadAll(file)
    if err != nil {
        fmt.Println("读取失败:", err)
        return
    }
    fmt.Println(string(content2))
}

按字节读取

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.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]))
}

循环读取

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file.Close()

    buf := make([]byte, 128)

    for {
        n, err := file.Read(buf)
        if err != nil {
            if err == io.EOF {
                fmt.Println("\n文件读取完毕")
                break
            }
            fmt.Println("读取失败:", err)
            return
        }
        fmt.Print(string(buf[:n]))
    }
}

按行读取

package main

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

func main() {
    file, err := os.Open("test.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)
    }
}

使用 bufio.Reader

package main

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

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file.Close()

    reader := bufio.NewReader(file)

    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                if len(line) > 0 {
                    fmt.Print(line)
                }
                break
            }
            fmt.Println("读取失败:", err)
            return
        }
        fmt.Print(line)
    }
}

33.3 写入文件

写入整个文件

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("写入成功")
}

使用 Write 方法

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("output.txt")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    defer file.Close()

    data := []byte("Hello, Go!\n")
    n, err := file.Write(data)
    if err != nil {
        fmt.Println("写入失败:", err)
        return
    }

    fmt.Printf("写入了 %d 字节\n", n)
}

使用 WriteString

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("output.txt")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    defer file.Close()

    n, err := file.WriteString("Hello, Go!\n")
    if err != nil {
        fmt.Println("写入失败:", err)
        return
    }

    fmt.Printf("写入了 %d 字节\n", n)
}

追加写入

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.OpenFile("output.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file.Close()

    _, err = file.WriteString("追加的内容\n")
    if err != nil {
        fmt.Println("写入失败:", err)
        return
    }

    fmt.Println("追加成功")
}

使用 bufio.Writer

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)

    lines := []string{
        "第一行",
        "第二行",
        "第三行",
    }

    for _, line := range lines {
        _, err := writer.WriteString(line + "\n")
        if err != nil {
            fmt.Println("写入失败:", err)
            return
        }
    }

    // 刷新缓冲区
    writer.Flush()

    fmt.Println("写入成功")
}

33.4 文件定位

Seek 方法

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("打开失败:", err)
        return
    }
    defer file.Close()

    // 移动到文件开头
    offset, err := file.Seek(0, 0)
    fmt.Printf("当前位置: %d\n", offset)

    // 移动到文件末尾
    offset, err = file.Seek(0, 2)
    fmt.Printf("文件大小: %d\n", offset)

    // 移动到文件开头后 10 字节
    offset, err = file.Seek(10, 0)
    fmt.Printf("当前位置: %d\n", offset)

    // 从当前位置向后移动 5 字节
    offset, err = file.Seek(5, 1)
    fmt.Printf("当前位置: %d\n", offset)
}

Seek 模式

模式说明
io.SeekStart (0)相对于文件开头
io.SeekCurrent (1)相对于当前位置
io.SeekEnd (2)相对于文件末尾

33.5 文件信息

获取文件信息

package main

import (
    "fmt"
    "os"
)

func main() {
    fileInfo, err := os.Stat("test.txt")
    if err != nil {
        fmt.Println("获取信息失败:", err)
        return
    }

    fmt.Printf("文件名: %s\n", fileInfo.Name())
    fmt.Printf("文件大小: %d 字节\n", fileInfo.Size())
    fmt.Printf("修改时间: %v\n", fileInfo.ModTime())
    fmt.Printf("权限: %v\n", fileInfo.Mode())
    fmt.Printf("是否目录: %v\n", fileInfo.IsDir())
}

判断文件是否存在

package main

import (
    "errors"
    "fmt"
    "os"
)

func fileExists(filename string) bool {
    _, err := os.Stat(filename)
    if err == nil {
        return true
    }
    if errors.Is(err, os.ErrNotExist) {
        return false
    }
    return false
}

func main() {
    if fileExists("test.txt") {
        fmt.Println("文件存在")
    } else {
        fmt.Println("文件不存在")
    }
}

33.6 文件操作

创建文件

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建文件(如果存在则清空)
    file, err := os.Create("newfile.txt")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    defer file.Close()

    fmt.Println("文件创建成功")
}

删除文件

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Remove("test.txt")
    if err != nil {
        fmt.Println("删除失败:", err)
        return
    }

    fmt.Println("删除成功")
}

重命名文件

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Rename("old.txt", "new.txt")
    if err != nil {
        fmt.Println("重命名失败:", err)
        return
    }

    fmt.Println("重命名成功")
}

复制文件

package main

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

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

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

    _, err = io.Copy(destFile, sourceFile)
    return err
}

func main() {
    err := copyFile("source.txt", "dest.txt")
    if err != nil {
        fmt.Println("复制失败:", err)
        return
    }

    fmt.Println("复制成功")
}

33.7 目录操作

创建目录

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建单层目录
    err := os.Mkdir("testdir", 0755)
    if err != nil {
        fmt.Println("创建失败:", err)
    }

    // 创建多层目录
    err = os.MkdirAll("testdir/sub1/sub2", 0755)
    if err != nil {
        fmt.Println("创建失败:", err)
    }

    fmt.Println("目录创建成功")
}

删除目录

package main

import (
    "fmt"
    "os"
)

func main() {
    // 删除空目录
    err := os.Remove("testdir")
    if err != nil {
        fmt.Println("删除失败:", err)
    }

    // 删除目录及其内容
    err = os.RemoveAll("testdir")
    if err != nil {
        fmt.Println("删除失败:", err)
    }

    fmt.Println("删除成功")
}

读取目录

package main

import (
    "fmt"
    "os"
)

func main() {
    // 读取目录
    entries, err := os.ReadDir(".")
    if err != nil {
        fmt.Println("读取失败:", err)
        return
    }

    for _, entry := range entries {
        info, err := entry.Info()
        if err != nil {
            continue
        }

        typeStr := "文件"
        if entry.IsDir() {
            typeStr = "目录"
        }

        fmt.Printf("%s %s %d bytes\n", typeStr, entry.Name(), info.Size())
    }
}

遍历目录

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

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

        typeStr := "文件"
        if info.IsDir() {
            typeStr = "目录"
        }

        fmt.Printf("%s: %s\n", typeStr, path)
        return nil
    })
}

func main() {
    walkDir(".")
}

33.8 临时文件

创建临时文件

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建临时文件
    file, err := os.CreateTemp("", "example-*.txt")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    defer os.Remove(file.Name())
    defer file.Close()

    fmt.Println("临时文件:", file.Name())

    // 写入内容
    file.WriteString("临时内容")
}

创建临时目录

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建临时目录
    dir, err := os.MkdirTemp("", "example-")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    defer os.RemoveAll(dir)

    fmt.Println("临时目录:", dir)
}

33.9 文件路径操作

filepath 包

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path := "/home/user/documents/file.txt"

    // 获取文件名
    fmt.Println("文件名:", filepath.Base(path))

    // 获取目录
    fmt.Println("目录:", filepath.Dir(path))

    // 获取扩展名
    fmt.Println("扩展名:", filepath.Ext(path))

    // 连接路径
    fmt.Println("连接路径:", filepath.Join("a", "b", "c.txt"))

    // 获取绝对路径
    abs, _ := filepath.Abs(".")
    fmt.Println("绝对路径:", abs)

    // 匹配文件
    matches, _ := filepath.Glob("*.go")
    fmt.Println("Go 文件:", matches)
}

33.10 小结

本章详细介绍了 Go 语言的文件操作:

  1. 文件打开:使用 os.Openos.OpenFile
  2. 文件读取os.ReadFileio.ReadAllbufio.Scanner
  3. 文件写入os.WriteFilefile.Writebufio.Writer
  4. 文件定位:使用 Seek 方法
  5. 文件信息:使用 os.Stat 获取文件信息
  6. 文件操作:创建、删除、重命名、复制
  7. 目录操作:创建、删除、读取、遍历
  8. 临时文件:使用 os.CreateTempos.MkdirTemp
  9. 路径操作:使用 filepath

掌握文件操作是进行数据处理的基础。在下一章中,我们将学习更多关于 IO 操作的知识。