一、文件基础分类
1. 设备文件(标准输入 / 输出)
操作系统自带的默认打开设备文件,程序启动即可直接使用
- 标准输出设备 stdout:屏幕
fmt.Println()底层封装os.Stdout,向屏幕输出内容- 原生 API:
os.Stdout.WriteString("内容\n") - 可手动关闭:
os.Stdout.Close(),关闭后无法再打印输出
- 标准输入设备 stdin:键盘
fmt.Scan(&变量)从键盘读取用户输入- 底层对应
os.Stdin
2. 磁盘文件(存储在硬盘)
断电后数据持久保存,分为两类:
- 文本文件:记事本打开不乱码,由字符组成(
.txt、.md、.json) - 二进制文件:记事本打开乱码,存储原始二进制数据(图片、视频、exe、压缩包)
补充:文件存在的意义
内存是临时存储,程序退出 / 设备断电后数据直接丢失;磁盘文件可永久保存数据。
二、os 包核心文件操作 API
1. 创建文件两种方式
go
// 方式1:常用,根据文件名创建文件,权限0666,文件默认可读可写 func Create(name string) (*os.File, error) // 方式2:通过文件描述符创建文件,底层底层接口,业务开发极少使用 func NewFile(fd uintptr, name string) *os.File2. 打开文件两种方式
go
// 方式1:只读模式打开文件,底层封装OpenFile func Open(name string) (*os.File, error) // 方式2:自定义打开模式、权限,功能最全(读写/追加/创建都靠它) func OpenFile(name string, flag int, perm uint32) (*os.File, error)flag:打开模式标识(只读、只写、读写、追加等)perm:文件权限(Linux 权限数字,如 0666)
3. 文件基础操作能力
文件 API 支持四大核心操作:创建、打开、读、写、删除
三、标准设备文件(Stdin/Stdout)代码示例
go
package main import ( "fmt" "os" ) func main() { // 1. 标准输出打印(封装os.Stdout) fmt.Println("are u ok?") // 原生Stdout写入 os.Stdout.WriteString("are u ok?\n") // 关闭标准输出,关闭后无法打印 // os.Stdout.Close() // 2. 标准输入:键盘读取 var a int fmt.Println("请输入a:") fmt.Scan(&a) fmt.Println("a = ", a) }四、磁盘文件写入操作 os.Create + WriteString
知识点
os.Create(path)文件不存在则新建,存在则清空原有内容- 文件打开后必须关闭,推荐
defer f.Close()延迟关闭,避免文件句柄泄漏 f.WriteString(string)写入字符串,返回写入字节长度与错误
go
package main import ( "fmt" "os" ) // 向文件循环写入内容 func WriteFile(path string) { // 创建/清空文件 f, err := os.Create(path) if err != nil { fmt.Println("创建文件失败 err = ", err) return } // 函数结束自动关闭文件 defer f.Close() for i := 0; i < 10; i++ { buf := fmt.Sprintf("i = %d\n", i) n, err := f.WriteString(buf) if err != nil { fmt.Println("写入失败 err = ", err) } fmt.Printf("本次写入字节数:%d\n", n) } } func main() { path := "./demo.txt" WriteFile(path) }五、磁盘文件读取操作 os.Open + Read
核心要点
os.Open()只读打开文件,不能写入Read([]byte):将文件数据读取到字节切片缓冲区- 返回值
n:实际读到的字节数量,打印时截取buf[:n],避免多余空字节 io.EOF:文件读取完毕标记,读到 EOF 代表文件读完
go
package main import ( "fmt" "io" "os" ) func ReadFile(path string) { // 只读打开文件 f, err := os.Open(path) if err != nil { fmt.Println("打开文件失败 err = ", err) return } defer f.Close() // 开辟2KB缓冲区 buf := make([]byte, 1024*2) n, err1 := f.Read(buf) // 排除正常结束的EOF错误 if err1 != nil && err1 != io.EOF { fmt.Println("读取文件失败 err1 = ", err1) return } // 只打印实际读到的数据 fmt.Println("文件内容:", string(buf[:n])) } func main() { ReadFile("./demo.txt") }六、bufio 缓冲区:按行读取文件(大文件推荐)
作用
直接 Read 一次性读取全部内容,大文件占用内存高;bufio提供缓冲区,支持按行读取,节省内存。
go
package main import ( "bufio" "fmt" "io" "os" ) func ReadFileByLine(path string) { f, err := os.Open(path) if err != nil { fmt.Println("打开失败 err = ", err) return } defer f.Close() // 创建带缓冲读取器 r := bufio.NewReader(f) for { // 按换行符分割读取一行,包含\n lineBuf, err := r.ReadString('\n') if err != nil { // 读到文件末尾,跳出循环 if err == io.EOF { break } fmt.Println("读取行失败 err = ", err) return } fmt.Print(lineBuf) } }七、实战案例:文件拷贝工具(命令行传参)
功能说明
- 通过
os.Args获取命令行执行参数 - 参数格式:
程序名 源文件路径 目标文件路径 - 开辟 4KB 缓冲区循环读写,适合任意大小文件
- 自动校验参数数量、源文件与目标文件不能同名
go
package main import ( "fmt" "io" "os" ) func main() { // 获取命令行参数列表 args := os.Args // 参数必须是:程序名 src dst 共3个 if len(args) != 3 { fmt.Println("使用方式:go run main.go 源文件路径 目标文件路径") return } srcFileName := args[1] dstFileName := args[2] if srcFileName == dstFileName { fmt.Println("错误:源文件和目标文件不能相同!") return } // 打开源文件(只读) sF, err := os.Open(srcFileName) if err != nil { fmt.Println("打开源文件失败 err = ", err) return } // 创建目标文件(清空原有内容) dF, err := os.Create(dstFileName) if err != nil { fmt.Println("创建目标文件失败 err = ", err) return } // 延迟关闭两个文件 defer sF.Close() defer dF.Close() // 4KB缓冲区循环读写 buf := make([]byte, 4*1024) for { // 读源文件 n, err := sF.Read(buf) if err != nil { if err == io.EOF { // 文件读取完成,退出循环 break } fmt.Println("读取源文件出错 err = ", err) return } // 读到多少,写入多少到目标文件 _, err = dF.Write(buf[:n]) if err != nil { fmt.Println("写入目标文件出错 err = ", err) return } } fmt.Println("文件拷贝完成!") }八、高频易错点汇总(复习重点)
- 文件打开后必须关闭,优先使用
defer Close(),防止句柄泄露 os.Create()打开已有文件会清空内容;只读读取用os.Open()Read(buf)返回 n 为实际读取字节,输出必须截取buf[:n],否则会打印空占位字节- 文件读取报错要判断
err != io.EOF,读到末尾属于正常情况,不算错误 - 大文件不要一次性全部读取,使用
bufio按行读取,降低内存占用 os.Args第一个参数是程序自身名称,业务参数从下标 1、2 开始取- 标准输入输出设备无需手动 Open,程序启动自动打开
- 文本 / 二进制文件读写 API 无区分,底层全部操作字节切片
九、方案选型对比
表格
| 读取方式 | 适用场景 | 优缺点 |
|---|---|---|
file.Read()一次性读 | 小文件 | 代码简单,大文件占用内存高 |
bufio.NewReader按行读 | 日志、大文本文件 | 内存占用低,逐行处理,推荐业务使用 |
| 循环缓冲区读写(拷贝案例) | 图片、视频等二进制大文件 | 稳定通用,工业级文件复制方案 |
十、整体知识总结
- 文件分设备文件(标准输入输出)、磁盘文件(文本 / 二进制);磁盘文件持久化存储数据
- 创建文件:
os.Create();只读打开:os.Open();自定义模式打开:os.OpenFile() - 写入:
WriteString();基础读取:Read([]byte);按行读取:bufio.Reader.ReadString() - 文件拷贝核心逻辑:开辟固定大小缓冲区,循环读源文件、写目标文件
- 开发规范:
defer延迟关闭文件、处理io.EOF正常结束标记、校验文件操作错误