第11篇 文件操作——数据的持久化存储**作者**中文编程倡导者—— 李金雨联系方式wbtm2718qq.com**目标读者**编程入门零基础核心理念使用华为仓颉原生中文编程体验真正的国产编程语言一、为什么需要文件操作想象一下你写了一篇作文如果不保存关机后就没了你玩游戏的进度如果不存档下次就要从头开始你通讯录里的联系人如果不保存手机重启就全丢了文件操作就是让我们把数据保存到硬盘上这样即使程序关闭、电脑关机数据也不会丢失。这叫做持久化存储。二、生活中的文件操作例子1写日记打开日记本→ 打开文件写下今天的事→ 写入数据合上日记本→ 关闭文件以后翻看→ 读取文件例子2图书馆借书系统读取借阅记录文件→ 查看谁借了哪些书写入新的借阅记录→ 记录新书被借走修改归还日期→ 更新文件内容三、仓颉语言中的文件操作1. 写入文件import std.fs.* import std.io.* main() { // 要写入的内容 var 日记内容 今天是2024年1月15日星期一。 今天学习了仓颉编程语言感觉很有趣 虽然类型后置的语法有点不习惯但总体还是挺好懂的。 try { // 打开文件如果不存在则创建 var 文件 File.open(我的日记.txt, w) // 写入内容 文件.write(日记内容) // 关闭文件 文件.close() println(日记保存成功) } catch (异常: Exception) { println(保存失败${异常.message}) } }2. 读取文件import std.fs.* import std.io.* main() { try { // 以只读方式打开文件 var 文件 File.open(我的日记.txt, r) // 读取所有内容 var 内容 文件.readAll() // 关闭文件 文件.close() // 显示内容 println( 日记内容 ) println(内容) } catch (异常: Exception) { println(读取失败${异常.message}) } }3. 追加写入import std.fs.* main() { try { // 以追加模式打开文件a表示append var 文件 File.open(我的日记.txt, a) // 追加新内容 文件.write(\n\n--- 补充 ---\n) 文件.write(今天还学会了文件操作可以保存数据了) 文件.close() println(追加内容成功) } catch (异常: Exception) { println(追加失败${异常.message}) } }四、CSV文件操作CSVComma Separated Values逗号分隔值是一种简单的表格数据格式可以用Excel打开。写入学生成绩表import std.fs.* import std.collection.* class 学生成绩 { var 姓名: String var 语文: Int64 var 数学: Int64 var 英语: Int64 init(姓名: String, 语文: Int64, 数学: Int64, 英语: Int64) { this.姓名 姓名 this.语文 语文 this.数学 数学 this.英语 英语 } func 转为CSV行(): String { return ${姓名},${语文},${数学},${英语} } } main() { var 学生列表 ArrayList学生成绩() 学生列表.add(学生成绩(张三, 85, 92, 78)) 学生列表.add(学生成绩(李四, 90, 88, 95)) 学生列表.add(学生成绩(王五, 78, 85, 88)) try { var 文件 File.open(学生成绩表.csv, w) // 写入表头 文件.write(姓名,语文,数学,英语\n) // 写入数据 for (学生 in 学生列表) { 文件.write(学生.转为CSV行() \n) } 文件.close() println(成绩表保存成功) } catch (异常: Exception) { println(保存失败${异常.message}) } }生成的CSV文件内容姓名,语文,数学,英语 张三,85,92,78 李四,90,88,95 王五,78,85,88读取CSV文件import std.fs.* import std.collection.* import std.convert.* main() { try { var 文件 File.open(学生成绩表.csv, r) var 所有行 文件.readAll().split(\n) 文件.close() println( 学生成绩表 ) println(姓名\t语文\t数学\t英语\t总分) println(----------------------------------------) // 跳过表头从第1行开始读取 for (i in 1..所有行.size) { var 行 所有行[i] if (行.isEmpty()) { continue } var 字段 行.split(,) var 姓名 字段[0] var 语文 Int64.parse(字段[1]) var 数学 Int64.parse(字段[2]) var 英语 Int64.parse(字段[3]) var 总分 语文 数学 英语 println(${姓名}\t${语文}\t${数学}\t${英语}\t${总分}) } } catch (异常: Exception) { println(读取失败${异常.message}) } }五、JSON文件操作JSONJavaScript Object Notation是一种更现代的数据格式结构清晰易于阅读。保存配置信息import std.fs.* class 程序配置 { var 程序名称: String 学生管理系统 var 版本: String 1.0 var 作者: String 小明 var 最大学生数: Int64 100 var 自动保存: Bool true func 转为JSON(): String { return { 程序名称: ${程序名称}, 版本: ${版本}, 作者: ${作者}, 最大学生数: ${最大学生数}, 自动保存: ${自动保存} } } } main() { var 配置 程序配置() try { var 文件 File.open(配置.json, w) 文件.write(配置.转为JSON()) 文件.close() println(配置保存成功) } catch (异常: Exception) { println(保存失败${异常.message}) } }生成的JSON文件{程序名称:学生管理系统,版本:1.0,作者:小明,最大学生数:100,自动保存:true}六、语法设计讨论文件操作中的类型声明同学们在学习文件操作时我们再次遇到了仓颉的类型后置语法问题。看看文件操作的代码class 学生成绩 { var 姓名: String // 类型后置 var 语文: Int64 // 类型后置 var 数学: Int64 // 类型后置 var 英语: Int64 // 类型后置 init(姓名: String, 语文: Int64, 数学: Int64, 英语: Int64) { // 参数类型后置 // ... } func 转为CSV行(): String { // 返回值类型后置 // ... } } main() { var 学生列表: ArrayList学生成绩 // 泛型类型后置 // ... }按照中国人的语言习惯我们习惯说字符串类型的姓名、“整数类型的语文成绩”我们习惯说学生成绩列表类型的变量但仓颉的写法var 姓名: String→ 读作姓名字符串类型的定语后置ArrayList学生成绩→ 读作学生成绩类型的列表泛型也是后置如果仓颉能改进成C#风格// 假设的改进语法 class 学生成绩 { String 姓名 // 字符串类型的姓名 Int64 语文 // 整数类型的语文成绩 Int64 数学 Int64 英语 init(String 姓名, Int64 语文, Int64 数学, Int64 英语) { // ... } String 转为CSV行() { // 字符串类型的转为CSV行方法 // ... } } main() { ArrayList学生成绩 学生列表 // 学生成绩列表类型的学生列表 // ... }这样读起来多么自然“定义一个学生成绩类它有字符串类型的姓名属性整数类型的语文、数学、英语成绩属性…”华为在设计仓颉语言时采用了类似Rust的类型后置语法这就像是在说姓名字符串类型的而不是字符串类型的姓名。现代汉语都是定语前置的我们希望中文编程语言能真正符合中国人的语言习惯七、文件路径操作import std.fs.* import std.path.* main() { // 获取当前工作目录 var 当前目录 Path.current() println(当前目录${当前目录}) // 拼接路径 var 文件路径 Path.join(当前目录, 数据, 学生.txt) println(文件路径${文件路径}) // 检查文件是否存在 if (File.exists(我的日记.txt)) { println(文件存在) } else { println(文件不存在) } // 创建目录 try { Dir.create(备份数据) println(目录创建成功) } catch (异常: Exception) { println(创建目录失败${异常.message}) } }八、练习时间练习1日记本程序创建一个简单的日记本程序可以输入当天的日记内容将日记保存到以日期命名的文件中如2024-01-15.txt可以按日期读取以前的日记练习2通讯录管理创建一个通讯录程序可以添加联系人姓名、电话、邮箱可以删除联系人可以查找联系人所有数据保存到CSV文件中练习3记账本创建一个记账本程序记录每笔收入和支出日期、类型、金额、备注可以查看某个月的总收入和总支出数据保存到JSON文件中九、本课小结今天我们学习了文件操作的重要性实现数据的持久化存储写入文件创建新文件或覆盖已有文件读取文件从文件中获取数据追加写入在文件末尾添加内容CSV格式简单的表格数据格式JSON格式结构化的数据格式文件路径操作处理文件和目录的路径类型语法思考在文件操作中我们频繁地声明类属性、方法参数、返回值类型每一次都要面对类型后置的语法。希望未来的中文编程语言能让类型声明更符合中国人的说话习惯真正做到说人话十、课后作业完善学生管理系统的文件存储功能将学生信息保存到JSON文件中程序启动时自动读取学生信息添加学生、删除学生后自动保存创建一个单词本程序可以添加单词英文、中文释义、例句可以按字母顺序查看所有单词可以搜索单词数据保存到CSV文件中思考在你的日常生活中还有哪些数据需要持久化存储如何用文件操作来实现下篇预告第12篇《综合实战——制作一个学生管理系统》综合运用前面学到的所有知识完成一个完整的项目