文件与 IO 操作实战
文件操作是编程中最基础也最常用的需求。Go 语言的 io 包设计优雅,通过 io.Reader 和 io.Writer 接口实现了高度的抽象和复用。 1. 文件读取 1.1 一次性读取整个文件 1package main 2 3import ( 4 "fmt" 5 "os" 6) 7 8func main() { 9 // 方法一:os.ReadFile (推荐,Go 1.16+) 10 data, err := os.ReadFile("test.txt") 11 if err != nil { 12 panic(err) 13 } 14 fmt.Println(string(data)) 15 16 // 方法二:ioutil.ReadFile (已废弃,但仍可用) 17 // data, err := ioutil.ReadFile("test.txt") 18} 1.2 逐行读取(大文件) 1import ( 2 "bufio" 3 "fmt" 4 "os" 5) 6 7func main() { 8 file, err := os.Open("large.txt") 9 if err != nil { 10 panic(err) 11 } 12 defer file.Close() // 确保文件关闭 13 14 scanner := bufio.NewScanner(file) 15 for scanner.Scan() { 16 line := scanner.Text() 17 fmt.Println(line) 18 } 19 20 if err := scanner.Err(); err != nil { 21 panic(err) 22 } 23} 1.3 按块读取 1func main() { 2 file, _ := os.Open("data.bin") 3 defer file.Close() 4 5 buffer := make([]byte, 1024) // 每次读 1KB 6 for { 7 n, err := file.Read(buffer) 8 if err == io.EOF { 9 break // 文件读完 10 } 11 if err != nil { 12 panic(err) 13 } 14 fmt.Printf("Read %d bytes\n", n) 15 // 处理 buffer[:n] 16 } 17} 2. 文件写入 2.1 一次性写入 1func main() { 2 data := []byte("Hello, World!\n") 3 4 // 写入文件(会覆盖原文件) 5 // 0644 是文件权限:rw-r--r-- 6 err := os.WriteFile("output.txt", data, 0644) 7 if err != nil { 8 panic(err) 9 } 10} 2.2 追加写入 1func main() { 2 file, err := os.OpenFile("log.txt", 3 os.O_APPEND|os.O_CREATE|os.O_WRONLY, // 追加模式 4 0644) 5 if err != nil { 6 panic(err) 7 } 8 defer file.Close() 9 10 file.WriteString("New log entry\n") 11} 2.3 使用 bufio 缓冲写入 1func main() { 2 file, _ := os.Create("output.txt") 3 defer file.Close() 4 5 writer := bufio.NewWriter(file) 6 7 for i := 0; i < 1000; i++ { 8 writer.WriteString(fmt.Sprintf("Line %d\n", i)) 9 } 10 11 writer.Flush() // 重要:将缓冲区内容写入文件 12} 3. 文件与目录操作 3.1 检查文件是否存在 1func fileExists(path string) bool { 2 _, err := os.Stat(path) 3 return !os.IsNotExist(err) 4} 3.2 创建目录 1// 创建单层目录 2os.Mkdir("mydir", 0755) 3 4// 创建多层目录(类似 mkdir -p) 5os.MkdirAll("path/to/mydir", 0755) 3.3 遍历目录 1import ( 2 "fmt" 3 "os" 4 "path/filepath" 5) 6 7func main() { 8 // 方法一:filepath.Walk 9 filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 10 if err != nil { 11 return err 12 } 13 if !info.IsDir() { 14 fmt.Println("File:", path) 15 } 16 return nil 17 }) 18 19 // 方法二:os.ReadDir (Go 1.16+) 20 entries, _ := os.ReadDir(".") 21 for _, entry := range entries { 22 fmt.Println(entry.Name(), entry.IsDir()) 23 } 24} 3.4 删除文件/目录 1// 删除文件 2os.Remove("file.txt") 3 4// 删除目录及其所有内容(类似 rm -rf) 5os.RemoveAll("mydir") 4. io.Copy 文件拷贝 4.1 复制文件 1func copyFile(src, dst string) error { 2 source, err := os.Open(src) 3 if err != nil { 4 return err 5 } 6 defer source.Close() 7 8 destination, err := os.Create(dst) 9 if err != nil { 10 return err 11 } 12 defer destination.Close() 13 14 // io.Copy 高效复制 15 _, err = io.Copy(destination, source) 16 return err 17} 4.2 显示进度的复制 1type ProgressReader struct { 2 reader io.Reader 3 total int64 4 read int64 5} 6 7func (pr *ProgressReader) Read(p []byte) (int, error) { 8 n, err := pr.reader.Read(p) 9 pr.read += int64(n) 10 11 // 打印进度 12 fmt.Printf("\rProgress: %.2f%%", float64(pr.read)/float64(pr.total)*100) 13 14 return n, err 15} 5. 网络文件下载 1import ( 2 "io" 3 "net/http" 4 "os" 5) 6 7func downloadFile(url, filepath string) error { 8 // 发起 HTTP GET 请求 9 resp, err := http.Get(url) 10 if err != nil { 11 return err 12 } 13 defer resp.Body.Close() 14 15 // 创建文件 16 out, err := os.Create(filepath) 17 if err != nil { 18 return err 19 } 20 defer out.Close() 21 22 // 将响应体复制到文件 23 _, err = io.Copy(out, resp.Body) 24 return err 25} 26 27func main() { 28 downloadFile("https://example.com/file.zip", "file.zip") 29} 6. io.Reader 和 io.Writer 接口 6.1 理解接口 1type Reader interface { 2 Read(p []byte) (n int, err error) 3} 4 5type Writer interface { 6 Write(p []byte) (n int, err error) 7} 实现了这两个接口的类型: ...