写Go程序时,经常要处理配置文件、日志、用户数据这些文本内容。比如你做个小工具,想把 config.json 里的端口号读出来;或者写个日志分析脚本,需要逐行扫一遍 access.log —— 这些都绕不开「读文件」这一步。
1. 最简单的:ioutil.ReadFile(已弃用但很多人还在用)
Go 1.16 之前,很多人第一反应就是 ioutil.ReadFile。它一行搞定,适合小文件:
data, err := ioutil.ReadFile("settings.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
注意:这个函数在 Go 1.16+ 已被移到 os.ReadFile,老项目可能还见得到,新代码建议直接用下面这个。
2. 推荐新手入门:os.ReadFile
这是目前最简洁安全的方式,自动处理打开、读取、关闭全流程:
data, err := os.ReadFile("user.json")
if err != nil {
log.Printf("读取失败:%v", err)
return
}
var user map[string]interface{}
json.Unmarshal(data, &user)
适合读取几百KB以内的配置、JSON、YAML 等小文件,不占心力。
3. 按行读:bufio.Scanner(适合日志、列表类文本)
要是文件很大,比如一个 200MB 的 access.log,全读进内存就太莽了。这时候用 bufio.Scanner 一行一行来:
file, _ := os.Open("access.log")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "404") {
fmt.Println("发现404错误:", line)
}
}
它默认单行上限 64KB,够日常用;真有超长行,可以调 scanner.Buffer(make([]byte, 64), 1<<20) 扩容。
4. 流式读取:io.ReadFull 和 io.Copy(大文件搬运)
想把一个 ISO 镜像复制到 U 盘?或做文件校验?别 load 全部进内存,用 io.Copy 直接管道传输:
src, _ := os.Open("linux.iso")
dst, _ := os.Create("/mnt/usb/linux.iso")
defer src.Close()
defer dst.Close()
_, err := io.Copy(dst, src) // 边读边写,内存只占几KB
如果要精确读固定长度(比如解析二进制头),用 io.ReadFull 更稳妥。
5. 精确控制:os.Open + Read + Close(适合自定义逻辑)
有些场景得自己拿捏节奏,比如边读边解密、跳过BOM头、按块校验MD5……这时手动打开+读取更灵活:
file, err := os.Open("secret.enc")
if err != nil {
panic(err)
}
defer file.Close()
buf := make([]byte, 4096)
for {
n, err := file.Read(buf)
if n > 0 {
// 对 buf[:n] 做解密或处理
processChunk(buf[:n])
}
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
注意:Read 不保证一次读完全部内容,必须循环处理 n 字节数,这是新手常踩的坑。
小提醒
路径别写死,多用 filepath.Join("config", "db.yaml") 拼接;中文路径在 Windows 上记得用 UTF-8 编码(一般默认没问题);读取前先 os.Stat 判断文件是否存在,比靠 err 判断更直观。