Go 文件处理
文件处理是编程中的重要技能。Go 语言提供了丰富的标准库来处理文件和目录操作,包括读写文件、目录遍历、文件信息获取等功能。
📋 文件基础操作
创建和写入文件
go
package main
import (
"fmt"
"os"
)
func createAndWriteFile() {
fmt.Println("=== 创建和写入文件 ===")
// 1. 使用 os.Create 创建文件
file, err := os.Create("example.txt")
if err != nil {
fmt.Printf("创建文件失败: %v\n", err)
return
}
defer file.Close() // 确保文件被关闭
// 2. 写入字符串
content := "Hello, Go File Handling!\n这是第二行内容。\n"
bytesWritten, err := file.WriteString(content)
if err != nil {
fmt.Printf("写入文件失败: %v\n", err)
return
}
fmt.Printf("成功写入 %d 字节到文件\n", bytesWritten)
// 3. 写入字节数据
data := []byte("这是字节数据\n")
bytesWritten, err = file.Write(data)
if err != nil {
fmt.Printf("写入字节失败: %v\n", err)
return
}
fmt.Printf("成功写入 %d 字节数据\n", bytesWritten)
// 4. 同步到磁盘
err = file.Sync()
if err != nil {
fmt.Printf("同步文件失败: %v\n", err)
return
}
fmt.Println("文件写入完成")
}
func main() {
createAndWriteFile()
}读取文件
go
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func readFile() {
fmt.Println("=== 读取文件 ===")
// 1. 一次性读取整个文件
fmt.Println("方法1: 一次性读取整个文件")
content, err := os.ReadFile("example.txt")
if err != nil {
fmt.Printf("读取文件失败: %v\n", err)
return
}
fmt.Printf("文件内容:\n%s\n", content)
// 2. 使用 os.Open 和 Read
fmt.Println("方法2: 使用 os.Open 逐块读取")
file, err := os.Open("example.txt")
if err != nil {
fmt.Printf("打开文件失败: %v\n", err)
return
}
defer file.Close()
buffer := make([]byte, 1024)
for {
bytesRead, err := file.Read(buffer)
if err == io.EOF {
break // 文件读取完毕
}
if err != nil {
fmt.Printf("读取文件错误: %v\n", err)
break
}
fmt.Printf("读取 %d 字节: %s", bytesRead, buffer[:bytesRead])
}
// 3. 使用 bufio.Scanner 逐行读取
fmt.Println("\n方法3: 逐行读取")
file, err = os.Open("example.txt")
if err != nil {
fmt.Printf("打开文件失败: %v\n", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNumber := 1
for scanner.Scan() {
line := scanner.Text()
fmt.Printf("第 %d 行: %s\n", lineNumber, line)
lineNumber++
}
if err := scanner.Err(); err != nil {
fmt.Printf("扫描文件错误: %v\n", err)
}
}
func main() {
readFile()
}📁 目录操作
目录创建和遍历
go
package main
import (
"fmt"
"os"
"path/filepath"
)
func directoryOperations() {
fmt.Println("=== 目录操作 ===")
// 1. 创建目录
dirName := "test_directory"
err := os.Mkdir(dirName, 0755) // 0755 是权限模式
if err != nil && !os.IsExist(err) {
fmt.Printf("创建目录失败: %v\n", err)
return
}
fmt.Printf("目录 '%s' 创建成功\n", dirName)
// 2. 创建嵌套目录
nestedDir := "parent/child/grandchild"
err = os.MkdirAll(nestedDir, 0755)
if err != nil {
fmt.Printf("创建嵌套目录失败: %v\n", err)
return
}
fmt.Printf("嵌套目录 '%s' 创建成功\n", nestedDir)
// 3. 在目录中创建文件
testFiles := []string{
filepath.Join(dirName, "file1.txt"),
filepath.Join(dirName, "file2.log"),
filepath.Join(dirName, "data.json"),
filepath.Join(nestedDir, "nested_file.txt"),
}
for _, filename := range testFiles {
file, err := os.Create(filename)
if err != nil {
fmt.Printf("创建文件 %s 失败: %v\n", filename, err)
continue
}
file.WriteString(fmt.Sprintf("这是文件 %s 的内容\n", filename))
file.Close()
fmt.Printf("文件 '%s' 创建成功\n", filename)
}
// 4. 列出目录内容
fmt.Printf("\n'%s' 目录内容:\n", dirName)
entries, err := os.ReadDir(dirName)
if err != nil {
fmt.Printf("读取目录失败: %v\n", err)
return
}
for _, entry := range entries {
if entry.IsDir() {
fmt.Printf(" 📁 %s (目录)\n", entry.Name())
} else {
info, _ := entry.Info()
fmt.Printf(" 📄 %s (文件, %d 字节)\n", entry.Name(), info.Size())
}
}
}
// 递归遍历目录
func walkDirectory(rootDir string) {
fmt.Printf("\n=== 递归遍历目录 '%s' ===\n", rootDir)
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("访问 %s 时出错: %v\n", path, err)
return nil // 继续遍历其他文件
}
// 计算相对路径的层级
relPath, _ := filepath.Rel(rootDir, path)
level := len(filepath.SplitList(relPath)) - 1
indent := ""
for i := 0; i < level; i++ {
indent += " "
}
if info.IsDir() {
fmt.Printf("%s📁 %s/\n", indent, info.Name())
} else {
fmt.Printf("%s📄 %s (%d 字节)\n", indent, info.Name(), info.Size())
}
return nil
})
if err != nil {
fmt.Printf("遍历目录失败: %v\n", err)
}
}
func main() {
directoryOperations()
walkDirectory(".")
// 清理测试文件和目录
fmt.Println("\n=== 清理测试文件 ===")
os.RemoveAll("test_directory")
os.RemoveAll("parent")
os.Remove("example.txt")
fmt.Println("清理完成")
}📄 文件信息和权限
获取文件信息
go
package main
import (
"fmt"
"os"
"time"
)
func fileInfo() {
fmt.Println("=== 文件信息获取 ===")
// 创建测试文件
filename := "info_test.txt"
content := "这是用于测试文件信息的文件内容。\n包含多行文本。\n"
err := os.WriteFile(filename, []byte(content), 0644)
if err != nil {
fmt.Printf("创建测试文件失败: %v\n", err)
return
}
// 获取文件信息
info, err := os.Stat(filename)
if err != nil {
fmt.Printf("获取文件信息失败: %v\n", err)
return
}
fmt.Printf("文件名: %s\n", info.Name())
fmt.Printf("文件大小: %d 字节\n", info.Size())
fmt.Printf("文件权限: %s\n", info.Mode())
fmt.Printf("修改时间: %s\n", info.ModTime().Format("2006-01-02 15:04:05"))
fmt.Printf("是否为目录: %v\n", info.IsDir())
// 检查文件是否存在
fmt.Printf("\n=== 文件存在性检查 ===\n")
files := []string{filename, "不存在的文件.txt", ".", "/etc/passwd"}
for _, file := range files {
if _, err := os.Stat(file); err == nil {
fmt.Printf("✅ '%s' 存在\n", file)
} else if os.IsNotExist(err) {
fmt.Printf("❌ '%s' 不存在\n", file)
} else {
fmt.Printf("⚠️ '%s' 检查时出错: %v\n", file, err)
}
}
// 修改文件权限
fmt.Printf("\n=== 修改文件权限 ===\n")
fmt.Printf("原始权限: %s\n", info.Mode())
err = os.Chmod(filename, 0755)
if err != nil {
fmt.Printf("修改权限失败: %v\n", err)
} else {
newInfo, _ := os.Stat(filename)
fmt.Printf("新权限: %s\n", newInfo.Mode())
}
// 修改文件时间
fmt.Printf("\n=== 修改文件时间 ===\n")
fmt.Printf("原始修改时间: %s\n", info.ModTime().Format("2006-01-02 15:04:05"))
newTime := time.Now().Add(-24 * time.Hour) // 设置为24小时前
err = os.Chtimes(filename, newTime, newTime)
if err != nil {
fmt.Printf("修改时间失败: %v\n", err)
} else {
updatedInfo, _ := os.Stat(filename)
fmt.Printf("新修改时间: %s\n", updatedInfo.ModTime().Format("2006-01-02 15:04:05"))
}
// 清理
os.Remove(filename)
}
func main() {
fileInfo()
}🔄 文件操作进阶
文件复制和移动
go
package main
import (
"fmt"
"io"
"os"
"path/filepath"
)
// 复制文件
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)
if err != nil {
return err
}
// 同步到磁盘
return destFile.Sync()
}
// 复制文件并保持权限
func copyFileWithPerms(src, dst string) error {
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
err = copyFile(src, dst)
if err != nil {
return err
}
// 设置相同的权限
return os.Chmod(dst, srcInfo.Mode())
}
// 移动文件
func moveFile(src, dst string) error {
err := os.Rename(src, dst)
if err != nil {
// 如果重命名失败,尝试复制后删除
err = copyFile(src, dst)
if err != nil {
return err
}
return os.Remove(src)
}
return nil
}
func advancedFileOps() {
fmt.Println("=== 高级文件操作 ===")
// 创建测试文件
srcFile := "source.txt"
content := "这是源文件的内容\n包含多行数据\n用于测试文件操作"
err := os.WriteFile(srcFile, []byte(content), 0644)
if err != nil {
fmt.Printf("创建源文件失败: %v\n", err)
return
}
// 1. 复制文件
dstFile := "destination.txt"
fmt.Printf("复制文件: %s -> %s\n", srcFile, dstFile)
err = copyFileWithPerms(srcFile, dstFile)
if err != nil {
fmt.Printf("复制文件失败: %v\n", err)
return
}
// 验证复制结果
srcInfo, _ := os.Stat(srcFile)
dstInfo, _ := os.Stat(dstFile)
fmt.Printf("源文件大小: %d 字节\n", srcInfo.Size())
fmt.Printf("目标文件大小: %d 字节\n", dstInfo.Size())
fmt.Printf("复制成功: %v\n", srcInfo.Size() == dstInfo.Size())
// 2. 移动文件
movedFile := "moved.txt"
fmt.Printf("\n移动文件: %s -> %s\n", dstFile, movedFile)
err = moveFile(dstFile, movedFile)
if err != nil {
fmt.Printf("移动文件失败: %v\n", err)
return
}
// 验证移动结果
if _, err := os.Stat(dstFile); os.IsNotExist(err) {
fmt.Println("原文件已不存在 ✅")
}
if _, err := os.Stat(movedFile); err == nil {
fmt.Println("新文件存在 ✅")
}
// 3. 创建临时文件
fmt.Println("\n=== 临时文件操作 ===")
tempFile, err := os.CreateTemp("", "go_temp_*.txt")
if err != nil {
fmt.Printf("创建临时文件失败: %v\n", err)
return
}
tempFileName := tempFile.Name()
fmt.Printf("临时文件路径: %s\n", tempFileName)
// 写入临时文件
tempFile.WriteString("这是临时文件内容")
tempFile.Close()
// 读取临时文件
tempContent, err := os.ReadFile(tempFileName)
if err != nil {
fmt.Printf("读取临时文件失败: %v\n", err)
} else {
fmt.Printf("临时文件内容: %s\n", tempContent)
}
// 清理文件
fmt.Println("\n=== 清理测试文件 ===")
files := []string{srcFile, movedFile, tempFileName}
for _, file := range files {
if err := os.Remove(file); err != nil {
fmt.Printf("删除 %s 失败: %v\n", file, err)
} else {
fmt.Printf("删除 %s 成功\n", file)
}
}
}
func main() {
advancedFileOps()
}🎯 实际应用示例
日志文件管理器
go
package main
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
// 日志文件管理器
type LogManager struct {
logDir string
maxFiles int
maxSizeMB int64
filePattern string
}
// 创建日志管理器
func NewLogManager(dir string, maxFiles int, maxSizeMB int64) *LogManager {
return &LogManager{
logDir: dir,
maxFiles: maxFiles,
maxSizeMB: maxSizeMB,
filePattern: "app_*.log",
}
}
// 写入日志
func (lm *LogManager) WriteLog(message string) error {
// 确保日志目录存在
err := os.MkdirAll(lm.logDir, 0755)
if err != nil {
return err
}
// 获取当前日志文件
logFile := lm.getCurrentLogFile()
// 检查文件大小
if lm.shouldRotateLog(logFile) {
err = lm.rotateLog()
if err != nil {
return err
}
logFile = lm.getCurrentLogFile()
}
// 写入日志
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
timestamp := time.Now().Format("2006-01-02 15:04:05")
logEntry := fmt.Sprintf("[%s] %s\n", timestamp, message)
_, err = file.WriteString(logEntry)
return err
}
// 获取当前日志文件路径
func (lm *LogManager) getCurrentLogFile() string {
return filepath.Join(lm.logDir, "app_current.log")
}
// 检查是否需要轮转日志
func (lm *LogManager) shouldRotateLog(filename string) bool {
info, err := os.Stat(filename)
if err != nil {
return false // 文件不存在,不需要轮转
}
sizeMB := info.Size() / (1024 * 1024)
return sizeMB >= lm.maxSizeMB
}
// 轮转日志文件
func (lm *LogManager) rotateLog() error {
currentFile := lm.getCurrentLogFile()
// 检查当前文件是否存在
if _, err := os.Stat(currentFile); os.IsNotExist(err) {
return nil // 文件不存在,无需轮转
}
// 生成新的文件名
timestamp := time.Now().Format("20060102_150405")
rotatedFile := filepath.Join(lm.logDir, fmt.Sprintf("app_%s.log", timestamp))
// 重命名当前文件
err := os.Rename(currentFile, rotatedFile)
if err != nil {
return err
}
fmt.Printf("日志文件已轮转: %s\n", rotatedFile)
// 清理旧文件
return lm.cleanOldLogs()
}
// 清理旧的日志文件
func (lm *LogManager) cleanOldLogs() error {
files, err := filepath.Glob(filepath.Join(lm.logDir, lm.filePattern))
if err != nil {
return err
}
// 按修改时间排序
type fileInfo struct {
path string
modTime time.Time
}
var fileInfos []fileInfo
for _, file := range files {
info, err := os.Stat(file)
if err != nil {
continue
}
fileInfos = append(fileInfos, fileInfo{
path: file,
modTime: info.ModTime(),
})
}
// 按时间降序排序(最新的在前)
sort.Slice(fileInfos, func(i, j int) bool {
return fileInfos[i].modTime.After(fileInfos[j].modTime)
})
// 删除超出限制的文件
if len(fileInfos) > lm.maxFiles {
for i := lm.maxFiles; i < len(fileInfos); i++ {
err := os.Remove(fileInfos[i].path)
if err != nil {
fmt.Printf("删除旧日志文件失败 %s: %v\n", fileInfos[i].path, err)
} else {
fmt.Printf("删除旧日志文件: %s\n", fileInfos[i].path)
}
}
}
return nil
}
// 获取日志统计信息
func (lm *LogManager) GetStats() (map[string]interface{}, error) {
files, err := filepath.Glob(filepath.Join(lm.logDir, "app_*.log"))
if err != nil {
return nil, err
}
var totalSize int64
var fileCount int
var oldestFile, newestFile time.Time
for i, file := range files {
info, err := os.Stat(file)
if err != nil {
continue
}
totalSize += info.Size()
fileCount++
if i == 0 {
oldestFile = info.ModTime()
newestFile = info.ModTime()
} else {
if info.ModTime().Before(oldestFile) {
oldestFile = info.ModTime()
}
if info.ModTime().After(newestFile) {
newestFile = info.ModTime()
}
}
}
stats := map[string]interface{}{
"file_count": fileCount,
"total_size_mb": float64(totalSize) / (1024 * 1024),
"oldest_file": oldestFile.Format("2006-01-02 15:04:05"),
"newest_file": newestFile.Format("2006-01-02 15:04:05"),
}
return stats, nil
}
func demonstrateLogManager() {
fmt.Println("=== 日志文件管理器演示 ===")
// 创建日志管理器
logManager := NewLogManager("logs", 3, 1) // 最多3个文件,每个文件最大1MB
// 写入一些日志
messages := []string{
"应用程序启动",
"用户登录: user123",
"数据库连接成功",
"处理用户请求",
"生成报告完成",
"用户退出登录",
"应用程序关闭",
}
for _, msg := range messages {
err := logManager.WriteLog(msg)
if err != nil {
fmt.Printf("写入日志失败: %v\n", err)
continue
}
fmt.Printf("✅ 日志写入: %s\n", msg)
time.Sleep(100 * time.Millisecond) // 模拟时间间隔
}
// 模拟大量日志写入以触发轮转
fmt.Println("\n模拟大量日志写入...")
for i := 0; i < 100; i++ {
longMessage := fmt.Sprintf("这是一条很长的日志消息 #%d,包含大量数据: %s",
i, strings.Repeat("data ", 50))
logManager.WriteLog(longMessage)
if i%20 == 0 {
fmt.Printf("已写入 %d 条日志\n", i+1)
}
}
// 获取统计信息
fmt.Println("\n=== 日志统计信息 ===")
stats, err := logManager.GetStats()
if err != nil {
fmt.Printf("获取统计信息失败: %v\n", err)
} else {
for key, value := range stats {
fmt.Printf("%s: %v\n", key, value)
}
}
// 列出所有日志文件
fmt.Println("\n=== 日志文件列表 ===")
files, err := filepath.Glob("logs/app_*.log")
if err != nil {
fmt.Printf("获取文件列表失败: %v\n", err)
} else {
for _, file := range files {
info, _ := os.Stat(file)
fmt.Printf("📄 %s (%.2f MB)\n",
filepath.Base(file), float64(info.Size())/(1024*1024))
}
}
// 清理演示文件
fmt.Println("\n=== 清理演示文件 ===")
os.RemoveAll("logs")
fmt.Println("清理完成")
}
func main() {
demonstrateLogManager()
}🎓 小结
本章我们全面学习了 Go 语言的文件处理:
- ✅ 文件基础:创建、读取、写入文件的多种方法
- ✅ 目录操作:目录创建、遍历和管理
- ✅ 文件信息:获取文件属性、权限设置
- ✅ 高级操作:文件复制、移动、临时文件
- ✅ 实际应用:日志文件管理器的完整实现
掌握文件处理技能对于开发实际应用程序非常重要,特别是在处理配置文件、日志文件、数据文件等场景中。
接下来,我们将学习 Go 语言正则表达式,掌握强大的文本模式匹配工具。
文件处理建议
- 总是记得关闭文件,使用 defer 确保资源释放
- 合理处理文件操作中的错误
- 注意文件权限设置,遵循最小权限原则
- 对于大文件,考虑使用流式处理避免内存问题