练习
学完本章你应该掌握
- 能根据文件大小和处理目标,选择
os.ReadFile、File.Read、bufio.Scanner三种不同的读取方式。 - 能正确处理文件读写中的关键细节,例如
[]byte和string的转换、buffer[:n]的使用、io.EOF的含义、scanner.Err()的检查。 - 能使用
os.WriteFile和os.OpenFile完成覆盖写、追加写,以及常见打开方式和权限参数的组合。 - 能使用
io.Copy、os.MkdirAll、os.ReadDir、os.Remove、os.RemoveAll完成基础文件复制与目录管理。 - 能把前面学过的函数、结构体、切片、map、错误返回、字符串处理等知识自然地用进文件处理程序里。
- 能写出小型的文件工具,例如配置加载器、文件摘要生成器、成绩报告生成器和批量备份器。
简单
第 1 题:写入并读回一段课程说明
编写一个 Go 程序,完成下面要求:
- 使用
os.WriteFile创建文件lesson.txt - 向文件中写入文本:
Go 文件操作很重要 - 再使用
os.ReadFile读回这份文件内容 - 最后输出文件内容和字节长度
示例输出可以类似这样:
| |
查看参考答案
| |
说明:os.WriteFile 和 os.ReadFile 都适合处理小文件;读取结果是 []byte,要转成 string 才更适合按文本输出。
第 2 题:打印源码文件名和当前工作目录
编写一个 Go 程序,完成下面要求:
- 使用
runtime.Caller(0)获取当前源码文件路径 - 只输出文件名部分
- 再使用
os.Getwd()获取当前工作目录 - 最后分别输出这两项内容
示例输出可以类似这样:
| |
查看参考答案
| |
说明:runtime.Caller 反映的是源码位置,os.Getwd() 反映的是程序运行时的当前工作目录,这两个概念不要混用。
第 3 题:按固定大小分片读取文件
编写一个 Go 程序,完成下面要求:
- 先创建文件
chunks.txt,内容为abcdefghi - 再使用
os.Open打开这个文件 - 使用大小为
3的缓冲区分片读取 - 每读取到一段内容,就单独输出一行
期望输出:
| |
查看参考答案
| |
说明:分片读取时要始终使用 buffer[:n],因为最后一次读取的字节数不一定等于缓冲区长度。
第 4 题:逐行读取并输出行号
编写一个 Go 程序,完成下面要求:
- 创建文件
todo.txt - 写入下面三行内容:
| |
- 使用
bufio.Scanner按行读取 - 输出时为每一行加上行号
期望输出:
| |
查看参考答案
| |
说明:逐行读取文本文件时,bufio.Scanner 往往比手动分片读取更直观,特别适合日志、清单和普通文本。
第 5 题:追加写入学习日志
编写一个 Go 程序,完成下面要求:
- 使用
os.OpenFile以“创建 + 只写 + 追加”的方式打开study.log - 依次写入两行内容:
| |
- 写入完成后,把整个文件再读出来并输出
期望输出:
| |
查看参考答案
| |
说明:这里的关键是 os.O_APPEND,如果少了它,再次写入时就不再是“往后追加”的语义了。
一般
下面几题会自然用到前面学过的函数、切片、map、字符串处理和错误返回,但主考点仍然是文件的读取、写入、复制和目录操作。
第 6 题:修正一段分片读取程序
下面程序想把 data.txt 的内容按块读出来,但现在有两个明显问题:
- 最后一次读取可能会混入旧数据
- 读到文件末尾时会直接报错退出
请改正代码,让它最终输出:
| |
| |
查看参考答案
修正后的代码如下:
| |
两个关键点分别是:
- 输出时必须使用
buffer[:n],否则最后一次会把上轮残留数据一起带出来。 io.EOF表示正常读到文件末尾,不应该直接当成普通错误panic。
第 7 题:封装一个 copyFile 函数
编写一个 Go 程序,完成下面要求:
- 编写函数
copyFile(srcPath, dstPath string) (int64, error) - 函数内部使用
os.Open、os.Create、io.Copy - 返回复制的字节数和错误信息
- 在
main中先创建源文件note.txt,内容为copy me - 再把它复制到
backup.txt - 最后输出复制字节数,并读取
backup.txt验证内容
期望输出:
| |
查看参考答案
| |
说明:io.Copy 会返回实际复制的字节数,这很适合顺手做校验或打印复制结果。
第 8 题:创建、读取并删除练习目录
编写一个 Go 程序,完成下面要求:
- 创建目录结构:
| |
- 再创建文件
practice_dir/notes/day1/todo.txt - 读取
practice_dir这一层的目录项,并输出每个条目的名称和是否为目录 - 删除空目录
practice_dir/cache - 再次读取
practice_dir,输出剩余条目名称
提示:这道题会用到 os.MkdirAll、os.ReadDir、os.Remove。
查看参考答案
| |
说明:os.Remove 适合删除单个文件或空目录;如果目录里还有内容,通常要改用 os.RemoveAll。
第 9 题:实现 readNonEmptyLines
编写一个 Go 程序,完成下面要求:
- 编写函数
readNonEmptyLines(path string) ([]string, error) - 这个函数逐行读取文件
- 跳过空行和只包含空格的行
- 返回所有“真正有内容”的行
- 在
main中准备测试文件,内容如下:
| |
- 调用函数后输出返回的切片和数量
期望输出可以类似这样:
| |
查看参考答案
| |
说明:strings.TrimSpace 会把一行首尾的空格和换行影响去掉,所以很适合做“空行过滤”。
第 10 题:把配置文件读成 map
编写一个 Go 程序,完成下面要求:
- 编写函数
loadConfig(path string) (map[string]string, error) - 配置文件每行格式为
key=value - 空行要跳过
- 以
#开头的注释行也要跳过 - 在
main中准备下面这份配置文件:
| |
- 读取后输出:
| |
查看参考答案
| |
说明:strings.SplitN(line, "=", 2) 表示最多只切两段,适合处理这种简单的 key=value 文本格式。
进阶
下面四题会把文件读写和前面学过的结构体、函数、map、错误处理、字符串转换,以及并发知识自然组合起来,更接近真实的小工具场景。
第 11 题:生成文件摘要结构体
编写一个 Go 程序,完成下面要求:
- 定义结构体
FileSummary,字段包括:Name stringBytes intLines intNonEmptyLines int - 编写函数
buildFileSummary(path string) (FileSummary, error) - 统计文件名、字节数、总行数、非空行数
- 在
main中准备测试文件summary.txt,内容如下:
| |
- 调用函数后输出摘要信息
期望输出可以类似这样:
| |
查看参考答案
| |
说明:这里把 os.ReadFile 和 bufio.Scanner 组合起来了,前者适合快速拿字节数,后者更适合逐行统计。
第 12 题:从成绩文件生成统计报告
请完成一个稍微完整一点的小场景:
- 定义结构体
Student,字段包括:Name stringScore int - 准备成绩文件
scores.txt,内容如下:
| |
- 编写函数
loadStudents(path string) ([]Student, error) - 使用
bufio.Scanner按行读取,并把每一行解析成一个Student - 编写函数
buildReport(students []Student) string - 统计学生人数、及格人数、平均分、最高分学生
- 把报告写入
report.txt - 最后再读回
report.txt并输出
期望输出可以类似这样:
| |
查看参考答案
| |
说明:
strings.Fields可以把一行按空白切开,很适合这种“姓名 分数”的简单文本格式。strconv.Atoi用来把文本分数转换成整数,它不是本章重点,但在真实文件处理里很常见。
第 13 题:批量备份指定文件
编写一个 Go 程序,完成下面要求:
- 编写函数
copyFile(srcPath, dstPath string) error - 再编写函数
backupFiles(srcFiles []string, backupDir string) error backupFiles要先创建备份目录- 再把传入的多个源文件逐个复制到备份目录中
- 在
main中准备两个源文件:lesson1.txtlesson2.txt - 把它们备份到
backup_dir - 最后读取
backup_dir并输出文件名
期望输出可以类似这样:
| |
查看参考答案
| |
说明:这道题的重点是把“单文件复制”进一步封装成“多文件批量备份”,也就是把本章几个基础 API 组织成一个小工具。
第 14 题:实现一个并发批量备份器
请完成一个综合练习:
- 编写函数
copyFile(srcPath, dstPath string) error - 准备三个源文件:
a.txtb.txtc.txt - 创建备份目录
backup_async - 为每个源文件启动一个 goroutine 执行复制
- 使用
sync.WaitGroup等待所有复制任务结束 - 使用
resultCh收集每个任务的结果消息 - 最后输出所有结果消息
结果消息可以类似这样:
| |
说明:结果输出顺序不固定。
查看参考答案
| |
说明:这道题虽然把 goroutine、WaitGroup 和 channel 带进来了,但核心任务依然是文件复制;并发只是让你顺手复习前面学过的程序组织方式。