第11篇 文件操作——数据的持久化存储

**作者:**中文编程倡导者—— 李金雨
联系方式: wbtm2718@qq.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文件操作

CSV(Comma 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文件操作

JSON(JavaScript 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文件中

九、本课小结

今天我们学习了:

  1. 文件操作的重要性:实现数据的持久化存储
  2. 写入文件:创建新文件或覆盖已有文件
  3. 读取文件:从文件中获取数据
  4. 追加写入:在文件末尾添加内容
  5. CSV格式:简单的表格数据格式
  6. JSON格式:结构化的数据格式
  7. 文件路径操作:处理文件和目录的路径

类型语法思考:在文件操作中,我们频繁地声明类属性、方法参数、返回值类型,每一次都要面对类型后置的语法。希望未来的中文编程语言能让类型声明更符合中国人的说话习惯,真正做到"说人话"!

十、课后作业

  1. 完善"学生管理系统"的文件存储功能:

    • 将学生信息保存到JSON文件中
    • 程序启动时自动读取学生信息
    • 添加学生、删除学生后自动保存
  2. 创建一个"单词本"程序:

    • 可以添加单词(英文、中文释义、例句)
    • 可以按字母顺序查看所有单词
    • 可以搜索单词
    • 数据保存到CSV文件中
  3. 思考:在你的日常生活中,还有哪些数据需要持久化存储?如何用文件操作来实现?


下篇预告:第12篇《综合实战——制作一个学生管理系统》,综合运用前面学到的所有知识,完成一个完整的项目!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐