Go 文件与 I/O 操作完全指南
引言文件操作是任何编程语言都必须掌握的基础技能Go 语言在这方面的设计简洁而强大。Go 的 I/O 操作主要围绕io、os、ioutil、bufio和fmt这几个核心包展开。标准库的设计遵循 Unix 哲学一个工具做好一件事通过组合实现复杂功能。本文将系统性地介绍 Go 中的各类文件与 I/O 操作从基础的读写文件到高级的缓冲 I/O 和目录操作再到实际生产环境中的日志分析工具实现。一、os 包基础文件操作1.1 文件创建与打开Go 的os包提供了最底层的文件操作函数package main import ( os fmt ) func main() { // Create 创建文件如果存在则截断 file, err : os.Create(test.txt) if err ! nil { panic(err) } defer file.Close() fmt.Println(文件创建成功) // Open 以只读方式打开文件 file, err os.Open(test.txt) if err ! nil { panic(err) } defer file.Close() // OpenFile 更通用的打开方式 // 第二个参数是模式O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, O_CREATE, O_EXCL, O_TRUNC file, err os.OpenFile(test.txt, os.O_RDWR|os.O_APPEND, 0644) if err ! nil { panic(err) } defer file.Close() }1.2 文件读写使用os.File的 Read 和 Write 方法func fileReadWrite() error { // 写入数据 file, err : os.Create(data.txt) if err ! nil { return err } defer file.Close() data : []byte(Hello, Go文件操作\n第二行数据) n, err : file.Write(data) if err ! nil { return err } fmt.Printf(成功写入 %d 字节\n, n) // 使用 WriteString 写入字符串 n, err file.WriteString(第三行数据\n) if err ! nil { return err } fmt.Printf(成功写入字符串 %d 字节\n, n) // 读取数据 file.Seek(0, 0) // 将指针移到文件开头 content : make([]byte, 1024) n, err file.Read(content) if err ! nil err ! io.EOF { return err } fmt.Printf(读取到: %s\n, content[:n]) return nil }1.3 文件指针操作os.File内部维护一个文件指针表示当前读写位置func fileSeeking() error { file, err : os.Open(data.txt) if err ! nil { return err } defer file.Close() // 获取当前指针位置 offset, err : file.Seek(0, io.SeekCurrent) if err ! nil { return err } fmt.Printf(当前偏移: %d\n, offset) // Seek 设置指针位置 // 第二个参数0起始位置, 1当前位置, 2结束位置 _, err file.Seek(10, io.SeekStart) if err ! nil { return err } // 获取新的偏移量 offset, err file.Seek(0, io.SeekCurrent) if err ! nil { return err } fmt.Printf(移动后偏移: %d\n, offset) return nil }二、ioutil 与便捷读写函数2.1 完整读取文件ioutil包提供了更便捷的高级函数import ( io/ioutil fmt ) func readAllFile() error { // ReadFile 读取整个文件适合小文件 content, err : ioutil.ReadFile(data.txt) if err ! nil { return err } fmt.Printf(文件内容:\n%s\n, string(content)) return nil }ReadFile 底层原理// ioutil.ReadFile 的简化实现 func ReadFile(filename string) ([]byte, error) { f, err : os.Open(filename) if err ! nil { return nil, err } defer f.Close() // 尝试获取文件大小 stat, err : f.Stat() if err ! nil { return nil, err } // 如果文件大小已知预分配内存 size : stat.Size() if size 0 { return []byte{}, nil } data : make([]byte, size) for { n, err : f.Read(data) if n 0 { data append(data, data[:n]...) } if err io.EOF { break } if err ! nil { return nil, err } } return data, nil }2.2 完整写入文件func writeFile() error { data : []byte(这是要写入的内容\n第二行) // WriteFile 写入整个文件如果文件存在则覆盖 err : ioutil.WriteFile(output.txt, data, 0644) if err ! nil { return err } fmt.Println(文件写入成功) return nil }2.3 临时文件与目录func tempFileDemo() error { // TempDir 创建临时目录 dir, err : ioutil.TempDir(, myapp-*) if err ! nil { return err } defer os.RemoveAll(dir) // 使用完清理 fmt.Printf(创建临时目录: %s\n, dir) // TempFile 创建临时文件 file, err : ioutil.TempFile(dir, data-*.txt) if err ! nil { return err } defer os.Remove(file.Name()) // 清理文件 // 写入临时文件 file.WriteString(临时数据) file.Close() fmt.Printf(创建临时文件: %s\n, file.Name()) return nil }三、缓冲 I/Obufio3.1 为什么需要缓冲 I/O直接的文件读写每次都会触发系统调用对于大量小数据量的操作性能很差。bufio通过在内存中维护缓冲区来减少系统调用次数无缓冲 I/O: 应用 - [read() syscall] - 内核 - 磁盘 (每次读取一个字节) 应用 - [read() syscall] - 内核 - 磁盘 (每次读取一个字节) ... 有缓冲 I/O: 应用 - 读取缓冲区 - [read() syscall] - 内核 - 磁盘 (一次性读取大量数据) 应用 - 读取缓冲区 (内存操作极快) 应用 - 读取缓冲区 (内存操作极快) ...3.2 bufio.Readerfunc bufferedRead() error { file, err : os.Open(largefile.txt) if err ! nil { return err } defer file.Close() // 创建缓冲读取器8KB 缓冲区 reader : bufio.NewReaderSize(file, 8*1024) // 按行读取 for { line, err : reader.ReadString(\n) if err ! nil err ! io.EOF { return err } fmt.Print(line) if err io.EOF { break } } return nil }bufio.Reader的主要方法// Read 从缓冲区读取数据 func (b *Reader) Read(p []byte) (n int, err error) // ReadByte 读取单个字节 func (b *Reader) ReadByte() (byte, error) // ReadBytes 读取直到指定分隔符 func (b *Reader) ReadBytes(delim byte) ([]byte, error) // ReadString 读取直到指定分隔符返回字符串 func (b *Reader) ReadString(delim byte) (string, error) // ReadLine 读取一行不建议使用更推荐 ReadBytes func (b *Reader) ReadLine() ([]byte, bool, error) // ReadSlice 读取直到分隔符 func (b *Reader) ReadSlice(delim byte) ([]byte, error) // Peek 返回缓冲区中的前 n 个字节不移动指针 func (b *Reader) Peek(n int) ([]byte, error) // Discard 跳过前 n 个字节 func (b *Reader) Discard(n int) (discarded int, err error)3.3 bufio.Writerfunc bufferedWrite() error { file, err : os.Create(output.txt) if err ! nil { return err } defer file.Close() // 创建缓冲写入器 writer : bufio.NewWriterSize(file, 8*1024) // 写入数据 for i : 0; i 1000; i { _, err : writer.WriteString(fmt.Sprintf(第 %d 行数据\n, i)) if err ! nil { return err } } // 重要刷新缓冲区确保所有数据写入文件 err writer.Flush() if err ! nil { return err } return nil }3.4 Scanner 逐行处理对于按行分割的数据bufio.Scanner是最简洁的 APIfunc lineCounter(filename string) (int, error) { file, err : os.Open(filename) if err ! nil { return 0, err } defer file.Close() scanner : bufio.NewScanner(file) // 可选设置缓冲区大小默认 64KB const maxCapacity 1024 * 1024 // 1MB buf : make([]byte, maxCapacity) scanner.Buffer(buf, maxCapacity) // 可选设置分割函数默认按行分割 // scanner.Split(bufio.ScanLines) count : 0 for scanner.Scan() { count } if err : scanner.Err(); err ! nil { return 0, err } return count, nil }Scanner 工作原理数据流向 磁盘 - 内核缓冲区 - 用户缓冲区(64KB) - Scanner缓冲区(64KB) - 应用程序 Scanner 内部维护两个缓冲区 1. 用户缓冲区Read Bytes从内核读取的大块数据 2. Token 缓冲区当前正在处理的行 当 token 超过 64KB 时需要使用 Buffer() 扩展四、格式化 I/O4.1 fmt 包的格式化输出func fmtDemo() { // 基础格式化 fmt.Printf(字符串: %s, 整数: %d, 浮点: %.2f\n, hello, 42, 3.14159) // 常用动词 // %v 默认格式 // %v 结构体时显示字段名 // %#v Go 语法表示 // %T 类型 // %% 转义百分号 type User struct { Name string Age int } user : User{Alice, 30} fmt.Printf(%v\n, user) // {Alice 30} fmt.Printf(%v\n, user) // {Name:Alice Age:30} fmt.Printf(%#v\n, user) // main.User{Name:Alice, Age:30} fmt.Printf(%T\n, user) // main.User // 宽度和对齐 fmt.Printf(|%6s|%6d|%6.2f|\n, Hello, 42, 3.14) // | Hello| 42| 3.14| fmt.Printf(|%-6s|%-6d|%-6.2f|\n, Hello, 42, 3.14) // |Hello |42 |3.14 | // 进制转换 fmt.Printf(十进制: %d\n, 255) fmt.Printf(二进制: %b\n, 255) fmt.Printf(十六进制: %x\n, 255) fmt.Printf(八进制: %o\n, 255) }4.2 fmt 包的格式化输入func fmtScanDemo() { // 从标准输入扫描 var name string var age int var salary float64 fmt.Print(请输入姓名、年龄、薪资: ) // Scan 从空白分隔的输入中读取 fmt.Scan(name, age, salary) fmt.Printf(姓名: %s, 年龄: %d, 薪资: %.2f\n, name, age, salary) // Scanf 按格式解析 fmt.Print(请按格式输入(姓名,年龄,薪资): ) fmt.Scanf(%s,%d,%f, name, age, salary) fmt.Printf(姓名: %s, 年龄: %d, 薪资: %.2f\n, name, age, salary) // Scanln 读取一行知道行尾 fmt.Println(请输入姓名和年龄:) fmt.Scanln(name, age) fmt.Printf(姓名: %s, 年龄: %d\n, name, age) }4.3 bufio fmt 扫描文件func scanFile(filename string) error { file, err : os.Open(filename) if err ! nil { return err } defer file.Close() scanner : bufio.NewScanner(file) var name string var age int var score float64 var totalScore float64 var count int for scanner.Scan() { line : scanner.Text() // 跳过空行和注释 if len(line) 0 || line[0] # { continue } // 解析数据 _, err : fmt.Sscanf(line, %s %d %f, name, age, score) if err ! nil { continue } totalScore score count fmt.Printf(学生: %s, 年龄: %d, 成绩: %.2f\n, name, age, score) } if count 0 { fmt.Printf(平均成绩: %.2f\n, totalScore/float64(count)) } return scanner.Err() }五、目录操作与文件遍历5.1 目录基本操作import ( os path/filepath ) func dirOperations() error { // 创建目录 err : os.Mkdir(testdir, 0755) if err ! nil !os.IsExist(err) { return err } // 创建多层目录 err os.MkdirAll(a/b/c/d, 0755) if err ! nil { return err } // 读取目录内容 entries, err : os.ReadDir(.) if err ! nil { return err } for _, entry : range entries { fmt.Printf(%s\t, entry.Name()) if entry.IsDir() { fmt.Printf([DIR]) } else { info, _ : entry.Info() fmt.Printf([FILE] %d bytes, info.Size()) } fmt.Println() } // 删除目录 err os.Remove(testdir) if err ! nil !os.IsNotExist(err) { return err } // 删除目录树 err os.RemoveAll(a) if err ! nil !os.IsNotExist(err) { return err } return nil }5.2 递归遍历目录使用filepath.Walk或filepath.WalkDir// Walk 遍历目录树 func walkDemo() error { return filepath.Walk(., func(path string, info os.FileInfo, err error) error { if err ! nil { return err } // 获取相对路径 relPath, _ : filepath.Rel(., path) // 跳过隐藏文件和目录 if strings.HasPrefix(filepath.Base(path), .) { if info.IsDir() { return filepath.SkipDir } return nil } // 打印结构 indent : strings.Count(relPath, string(filepath.Separator)) for i : 0; i indent; i { fmt.Print( ) } if info.IsDir() { fmt.Printf( %s/\n, info.Name()) } else { fmt.Printf( %s (%d bytes)\n, info.Name(), info.Size()) } return nil }) } // WalkDir 更高效不调用 Stat 对于目录 func walkDirDemo() error { return filepath.WalkDir(., func(path string, d fs.DirEntry, err error) error { if err ! nil { return err } info, err : d.Info() if err ! nil { return err } fmt.Printf(%s: %d bytes\n, path, info.Size()) return nil }) }5.3 查找特定文件func findFiles(root, pattern string) ([]string, error) { var matches []string err : filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err ! nil { return err } // 匹配文件名模式 matched, err : filepath.Match(pattern, info.Name()) if err ! nil { return err } if matched { absPath, _ : filepath.Abs(path) matches append(matches, absPath) } return nil }) return matches, err }六、文件权限与属性6.1 文件权限Go 使用 Unix 风格的权限模型func permissionDemo() error { // 创建文件并设置权限 file, err : os.OpenFile(secure.txt, os.O_CREATE|os.O_WRONLY, 0600) if err ! nil { return err } file.Close() // 修改文件权限 err os.Chmod(secure.txt, 0644) if err ! nil { return err } // 修改目录权限 err os.Mkdir(rwx, 0755) if err ! nil { return err } err os.Chmod(rwx, 0700) return nil }6.2 文件属性获取func fileInfoDemo(filename string) error { info, err : os.Stat(filename) if err ! nil { return err } fmt.Printf(文件名: %s\n, info.Name()) fmt.Printf(大小: %d bytes\n, info.Size()) fmt.Printf(权限: %o\n, info.Mode().Perm()) fmt.Printf(是否目录: %t\n, info.IsDir()) fmt.Printf(修改时间: %s\n, info.ModTime().Format(2006-01-02 15:04:05)) // 获取更详细的权限信息 mode : info.Mode() fmt.Printf(是常规文件: %t\n, mode.IsRegular()) fmt.Printf(是目录: %t\n, mode.IsDir()) fmt.Printf(是符号链接: %t\n, modeos.ModeSymlink ! 0) return nil }6.3 文件时间戳func fileTimeDemo() error { // 获取文件访问和修改时间 info, err : os.Stat(data.txt) if err ! nil { return err } modTime : info.ModTime() accessTime : statAtime(info.Sys().(*syscall.Stat_t)) fmt.Printf(修改时间: %s\n, modTime) fmt.Printf(访问时间: %s\n, accessTime) // 设置文件时间戳仅 Unix // os.Chtimes(data.txt, time.Now(), time.Now()) return nil }七、实战案例日志分析工具7.1 需求分析我们需要实现一个日志分析工具具备以下功能多格式支持支持 Nginx、Apache JSON 格式日志实时统计访问量、状态码分布、Top N IP模式匹配支持正则表达式过滤性能优化使用 goroutine 并行处理7.2 完整实现package main import ( bufio bytes context encoding/json flag fmt io log mime mime/quotedprintable net os path/filepath regexp runtime sort strings sync sync/atomic time ) // 日志条目 type LogEntry struct { Timestamp time.Time Method string Path string Status int Size int64 ClientIP string UserAgent string Referer string Protocol string ResponseTime float64 } // 统计信息 type Stats struct { TotalRequests int64 TotalBytes int64 StatusCounts map[int]int64 MethodCounts map[string]int64 TopIPs map[string]int64 TopPaths map[string]int64 mu sync.RWMutex } func NewStats() *Stats { return Stats{ StatusCounts: make(map[int]int64), MethodCounts: make(map[string]int64), TopIPs: make(map[string]int64), TopPaths: make(map[string]int64), } } func (s *Stats) Increment(status int, method, ip, path string, size int64) { atomic.AddInt64(s.TotalRequests, 1) atomic.AddInt64(s.TotalBytes, size) s.mu.Lock() s.StatusCounts[status] s.MethodCounts[method] s.TopIPs[ip] s.TopPaths[path] s.mu.Unlock() } type LogParser struct { stats *Stats pattern *regexp.Regexp workers int entryChan chan *LogEntry resultChan chan *Stats ctx context.Context cancel context.CancelFunc } func NewLogParser(workers int) *LogParser { ctx, cancel : context.WithCancel(context.Background()) return LogParser{ stats: NewStats(), workers: workers, entryChan: make(chan *LogEntry, 10000), resultChan: make(chan *Stats, workers), ctx: ctx, cancel: cancel, } } // Nginx 日志格式解析 // 192.168.1.1 - - [10/Oct/2026:13:55:36 0000] GET /api/users HTTP/1.1 200 1234 http://example.com Mozilla/5.0 var nginxPattern regexp.MustCompile((?Pip[\d\.]) - \S \[(?Ptimestamp[^\]])\] (?Pmethod\S) (?Ppath\S) (?Pprotocol\S) (?Pstatus\d) (?Psize\d) (?Preferer[^]*) (?Puser_agent[^]*)) // Apache 日志格式解析 // 127.0.0.1 - frank [10/Oct/2026:13:55:36 0000] GET /apache_pb.gif HTTP/1.0 200 2326 var apachePattern regexp.MustCompile((?Pip[\d\.]) \S \S \[(?Ptimestamp[^\]])\] (?Pmethod\S) (?Ppath\S) (?Pprotocol\S) (?Pstatus\d) (?Psize\d)) // JSON 日志格式解析 func parseJSONLog(line []byte) (*LogEntry, error) { var entry LogEntry if err : json.Unmarshal(line, entry); err ! nil { return nil, err } return entry, nil } func (p *LogParser) parseNginxLog(line string) (*LogEntry, error) { matches : nginxPattern.FindStringSubmatch(line) if matches nil { return nil, fmt.Errorf(无法解析日志行) } timestamp, err : time.Parse(02/Jan/2006:15:04:05 -0700, nginxPattern.SubexpNames()[1]) if err ! nil { // 尝试 RFC3339 格式 timestamp time.Now() } entry : LogEntry{ Timestamp: timestamp, Method: nginxPattern.SubexpNames()[2], Path: nginxPattern.SubexpNames()[3], Protocol: nginxPattern.SubexpNames()[4], } fmt.Sscanf(nginxPattern.SubexpNames()[5], %d, entry.Status) fmt.Sscanf(nginxPattern.SubexpNames()[6], %d, entry.Size) entry.ClientIP nginxPattern.SubexpNames()[0] entry.Referer nginxPattern.SubexpNames()[7] entry.UserAgent nginxPattern.SubexpNames()[8] return entry, nil } func (p *LogParser) parseLine(line string) (*LogEntry, error) { // JSON 格式检测 if len(line) 0 line[0] { { return parseJSONLog([]byte(line)) } // 尝试 Nginx 格式 if strings.Contains(line, [) strings.Contains(line, \GET) { return p.parseNginxLog(line) } return nil, fmt.Errorf(未知格式) } func (p *LogParser) processFile(filename string) error { file, err : os.Open(filename) if err ! nil { return fmt.Errorf(打开文件失败 %s: %w, filename, err) } defer file.Close() // 使用 Scanner 按行处理 scanner : bufio.NewScanner(file) buf : make([]byte, 0, 64*1024) scanner.Buffer(buf, 1024*1024) lineNum : 0 for scanner.Scan() { lineNum line : scanner.Text() // 跳过空行 if len(strings.TrimSpace(line)) 0 { continue } // 正则过滤 if p.pattern ! nil !p.pattern.MatchString(line) { continue } entry, err : p.parseLine(line) if err ! nil { log.Printf(解析错误 [文件: %s, 行: %d]: %v, filename, lineNum, err) continue } p.stats.Increment(entry.Status, entry.Method, entry.ClientIP, entry.Path, entry.Size) } return scanner.Err() } func (p *LogParser) processFileWorker(files -chan string) { localStats : NewStats() for file : range files { err : p.parseFileStats(file, localStats) if err ! nil { log.Printf(处理文件失败: %s, 错误: %v, file, err) } } p.resultChan - localStats } func (p *LogParser) parseFileStats(filename string, stats *Stats) error { file, err : os.Open(filename) if err ! nil { return err } defer file.Close() scanner : bufio.NewScanner(file) for scanner.Scan() { line : scanner.Text() if len(strings.TrimSpace(line)) 0 { continue } if p.pattern ! nil !p.pattern.MatchString(line) { continue } entry, err : p.parseLine(line) if err ! nil { continue } stats.Increment(entry.Status, entry.Method, entry.ClientIP, entry.Path, entry.Size) } return scanner.Err() } func (p *LogParser) mergeStats(workerStats []*Stats) { for _, ws : range workerStats { p.stats.mu.Lock() for k, v : range ws.StatusCounts { p.stats.StatusCounts[k] v } for k, v : range ws.MethodCounts { p.stats.MethodCounts[k] v } for k, v : range ws.TopIPs { p.stats.TopIPs[k] v } for k, v : range ws.TopPaths { p.stats.TopPaths[k] v } p.stats.mu.Unlock() } } func (p *LogParser) Parse(pattern string, paths []string) error { // 编译过滤模式 if pattern ! { p.pattern regexp.MustCompile(pattern) } // 收集所有日志文件 var files []string for _, path : range paths { info, err : os.Stat(path) if err ! nil { return err } if info.IsDir() { dirFiles, err : collectLogFiles(path) if err ! nil { return err } files append(files, dirFiles...) } else { files append(files, path) } } if len(files) 0 { return fmt.Errorf(没有找到日志文件) } log.Printf(找到 %d 个日志文件使用 %d 个工作协程, len(files), p.workers) // 创建文件通道 fileChan : make(chan string, len(files)) for _, f : range files { fileChan - f } close(fileChan) // 启动工作协程 var wg sync.WaitGroup for i : 0; i p.workers; i { wg.Add(1) go func() { defer wg.Done() p.processFileWorker(fileChan) }() } // 等待所有工作完成并收集结果 go func() { wg.Wait() close(p.resultChan) }() var workerStats []*Stats for stats : range p.resultChan { workerStats append(workerStats, stats) } p.mergeStats(workerStats) return nil } func collectLogFiles(dir string) ([]string, error) { var files []string pattern : filepath.Join(dir, *.log) matches, err : filepath.Glob(pattern) if err ! nil { return nil, err } files append(files, matches...) // 递归处理子目录 entries, err : os.ReadDir(dir) if err ! nil { return nil, err } for _, entry : range entries { if entry.IsDir() { subFiles, err : collectLogFiles(filepath.Join(dir, entry.Name())) if err ! nil { continue } files append(files, subFiles...) } } return files, nil } func (s *Stats) Print() { s.mu.Lock() defer s.mu.Unlock() fmt.Println(\n strings.Repeat(, 60)) fmt.Println( 日志分析报告) fmt.Println(strings.Repeat(, 60)) fmt.Printf(\n总请求数: %d\n, atomic.LoadInt64(s.TotalRequests)) fmt.Printf(总流量: %s\n, formatBytes(atomic.LoadInt64(s.TotalBytes))) fmt.Println(\n--- 状态码分布 ---) var statusCodes []int for code : range s.StatusCounts { statusCodes append(statusCodes, code) } sort.Ints(statusCodes) for _, code : range statusCodes { count : s.StatusCounts[code] pct : float64(count) / float64(atomic.LoadInt64(s.TotalRequests)) * 100 fmt.Printf( %d: %d (%.1f%%) %s\n, code, count, pct, statusCodeDesc(code)) } fmt.Println(\n--- 请求方法分布 ---) for method, count : range s.MethodCounts { pct : float64(count) / float64(atomic.LoadInt64(s.TotalRequests)) * 100 fmt.Printf( %s: %d (%.1f%%)\n, method, count, pct) } fmt.Println(\n--- Top 10 IP 地址 ---) printTopN(s.TopIPs, 10) fmt.Println(\n--- Top 10 请求路径 ---) printTopN(s.TopPaths, 10) fmt.Println(strings.Repeat(, 60)) } func printTopN(m map[string]int64, n int) { type kv struct { Key string Value int64 } var ss []kv for k, v : range m { ss append(ss, kv{k, v}) } sort.Slice(ss, func(i, j int) bool { return ss[i].Value ss[j].Value }) for i : 0; i n i len(ss); i { fmt.Printf( %s: %d\n, ss[i].Key, ss[i].Value) } } func statusCodeDesc(code int) string { switch { case code 200 code 300: return 成功 case code 300 code 400: return 重定向 case code 400 code 500: return 客户端错误 case code 500: return 服务端错误 default: return 未知 } } func formatBytes(bytes int64) string { const unit 1024 if bytes unit { return fmt.Sprintf(%d B, bytes) } div, exp : int64(unit), 0 for n : bytes / unit; n unit; n / unit { div * unit exp } return fmt.Sprintf(%.1f %cB, float64(bytes)/float64(div), KMGTPE[exp]) } func main() { workers : flag.Int(w, runtime.NumCPU(), 工作协程数量) pattern : flag.String(filter, , 正则表达式过滤) flag.Parse() paths : flag.Args() if len(paths) 0 { fmt.Println(用法: loganalyzer [选项] 日志文件或目录) flag.PrintDefaults() os.Exit(1) } parser : NewLogParser(*workers) start : time.Now() if err : parser.Parse(*pattern, paths); err ! nil { log.Fatalf(分析失败: %v, err) } parser.stats.Print() fmt.Printf(\n分析耗时: %v\n, time.Since(start)) }7.3 使用示例与测试创建测试日志文件# 创建测试数据 mkdir -p logs cat logs/access.log EOF 192.168.1.1 - - [10/Oct/2026:13:55:36 0000] GET /api/users HTTP/1.1 200 1234 http://example.com Mozilla/5.0 192.168.1.2 - - [10/Oct/2026:13:55:37 0000] POST /api/login HTTP/1.1 200 512 http://example.com Mozilla/5.0 192.168.1.1 - - [10/Oct/2026:13:55:38 0000] GET /api/users/123 HTTP/1.1 404 256 http://example.com Mozilla/5.0 192.168.1.3 - - [10/Oct/2026:13:55:39 0000] GET /static/app.js HTTP/1.1 200 10240 http://example.com Mozilla/5.0 192.168.1.1 - - [10/Oct/2026:13:55:40 0000] GET /api/products HTTP/1.1 500 128 http://example.com Mozilla/5.0 EOF运行分析工具go run main.go logs/ # 带过滤条件的分析 go run main.go -filter /api/ logs/ # 指定工作协程数 go run main.go -w 8 logs/7.4 性能优化要点1. 缓冲区大小选择// 小文件默认缓冲区足够 scanner : bufio.NewScanner(file) // 大文件增加缓冲区避免内存压力 scanner : bufio.NewScanner(file) buf : make([]byte, 0, 64*1024) // 64KB scanner.Buffer(buf, 1024*1024) // 最大 token 1MB2. 并行处理策略文件级并行 File1 - Worker1 - Stats1 File2 - Worker2 - Stats2 File3 - Worker3 - Stats3 ↓ Merge Results 行级并行需要更复杂的实现 Scanner - Channel - Workers - Reduce3. 内存优化// 避免在循环中分配大对象 for scanner.Scan() { // 复用buffer line : scanner.Bytes() process(line) // 使用字节切片而非字符串拷贝 }八、实战案例配置文件的读写8.1 INI 配置文件package config import ( bufio fmt os regexp strings ) type Config struct { sections map[string]map[string]string } func NewConfig() *Config { return Config{ sections: make(map[string]map[string]string), } } func (c *Config) Load(filename string) error { file, err : os.Open(filename) if err ! nil { return err } defer file.Close() var currentSection string sectionRE : regexp.MustCompile(^\[(.)\]$) kvRE : regexp.MustCompile(^([^])(.*)$) scanner : bufio.NewScanner(file) for scanner.Scan() { line : strings.TrimSpace(scanner.Text()) // 跳过空行和注释 if line || strings.HasPrefix(line, #) || strings.HasPrefix(line, ;) { continue } // 解析节 if matches : sectionRE.FindStringSubmatch(line); matches ! nil { currentSection matches[1] if _, ok : c.sections[currentSection]; !ok { c.sections[currentSection] make(map[string]string) } continue } // 解析键值对 if matches : kvRE.FindStringSubmatch(line); matches ! nil { key : strings.TrimSpace(matches[1]) value : strings.TrimSpace(matches[2]) value strings.Trim(value, \) if currentSection { currentSection default c.sections[currentSection] make(map[string]string) } c.sections[currentSection][key] value } } return scanner.Err() } func (c *Config) Save(filename string) error { file, err : os.Create(filename) if err ! nil { return err } defer file.Close() writer : bufio.NewWriter(file) for section, kv : range c.sections { fmt.Fprintf(writer, [%s]\n, section) for k, v : range kv { fmt.Fprintf(writer, %s %q\n, k, v) } fmt.Fprintln(writer) } return writer.Flush() } func (c *Config) Get(section, key string) string { if s, ok : c.sections[section]; ok { if v, ok : s[key]; ok { return v } } return } func (c *Config) Set(section, key, value string) { if _, ok : c.sections[section]; !ok { c.sections[section] make(map[string]string) } c.sections[section][key] value }总结Go 的 I/O 系统设计得非常优雅通过组合不同的包和接口我们可以构建出高效、灵活的文件处理方案基础 I/O使用os包进行底层的文件操作便捷函数ioutil包提供常用的高层操作缓冲 I/Obufio包优化大量小读写的性能格式化 I/Ofmt包处理格式化的输入输出目录操作os和path/filepath包处理目录和路径在实际项目中选择合适的 I/O 方式需要考虑数据量大小大文件使用流式处理避免一次性加载性能要求频繁小读写使用bufio偶尔一次用ioutil并发安全文件操作本身非线程安全需要加锁或使用sync.Mutex错误处理始终检查返回值特别是 I/O 操作
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2583255.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!