Skip to content

文件(wenjian / fs) ​

读写文件、操作目录、查询文件信息。

cpro
daoru "wenjian"
cpro
import "fs"

模块名可以写 wenjian 或 fs,导入时用哪个,调用时就用哪个。

快速上手 ​

读文本文件

cpro
daoru "wenjian"

rukou() {
    changshi {
        bl text = wenjian.du_wenben("config.txt")
        dayin(text)
    } buhuo (e) {
        dayin(e.xinxi)
    }
}
cpro
import "fs"

rukou() {
    try {
        var text = fs.read_to_string("config.txt")
        print(text)
    } catch (e) {
        print(e.msg)
    }
}

逐行处理

cpro
changshi {
    bl text = wenjian.du_wenben("data.txt")
    bl lines = text.fenhang()       // 先存进变量才能遍历
    jishu (line shuyu lines) {
        dayin(line)
    }
} buhuo (e) {
    dayin(e.xinxi)
}
cpro
try {
    var text = fs.read_to_string("data.txt")
    var lines = text.lines()        // 先存进变量才能遍历
    for (line in lines) {
        print(line)
    }
} catch (e) {
    print(e.msg)
}

写文件

cpro
changshi {
    wenjian.xie_wenben("out.txt", "hello\n")         // 覆盖,不存在则创建
    wenjian.zhuijia_wenben("log.txt", "一行日志\n")  // 追加到末尾
} buhuo (e) {
    dayin(e.xinxi)
}
cpro
try {
    fs.write_string("out.txt", "hello\n")       // 覆盖,不存在则创建
    fs.append_string("log.txt", "一行日志\n")   // 追加到末尾
} catch (e) {
    print(e.msg)
}

区分"文件不存在"和其他错误

cpro
changshi {
    bl text = wenjian.du_wenben("maybe.txt")
    dayin(text)
} buhuo (e) {
    ruguo (wenjian.shi_bu_cunzai(e.daima)) {
        dayin("文件不存在,使用默认配置")
    } fouze {
        dayin(e.xinxi)
    }
}
cpro
try {
    var text = fs.read_to_string("maybe.txt")
    print(text)
} catch (e) {
    if (fs.is_not_found(e.code)) {
        print("文件不存在,使用默认配置")
    } else {
        print(e.msg)
    }
}

读写整个文件 ​

拼音英文签名
du_wenbenread_to_string(path zifuchuan) zifuchuan kecuo
xie_wenbenwrite_string(path zifuchuan, content zifuchuan) zhenjia kecuo
zhuijia_wenbenappend_string(path zifuchuan, content zifuchuan) zhenjia kecuo
yuanzi_xie_wenbenwrite_string_atomic(path zifuchuan, content zifuchuan) zhenjia kecuo
du_zijieread_bytes(path zifuchuan) [w8] kecuo
xie_zijiewrite_bytes(path zifuchuan, data [w8]) zhenjia kecuo
  • xie_wenben 是覆盖写:文件不存在就创建,存在就清空后再写
  • 内容原样读写,不转换换行符
  • du_wenben 要求文件内容是合法 UTF-8;读二进制文件请用 du_zijie
  • 返回 zhenjia 的函数成功时总是 zheng,失败会进入 buhuo,不用检查返回值

原子写 ​

yuanzi_xie_wenben 先写到同目录下的临时文件,写完再改名替换目标文件。中途出错时目标文件保持原样,任何时候读到的要么是旧内容、要么是完整的新内容。

配置文件、索引这类不能只写一半的文件,请用它。代价是多一次写入和改名,所以不是默认行为。

文件信息与目录 ​

拼音英文签名说明
cunzaiexists(path zifuchuan) zhenjia是否存在
shi_muluis_dir(path zifuchuan) zhenjia是否为目录
wenjian_daxiaofile_size(path zifuchuan) z64 kecuo文件大小(字节)
xiugai_shijianmtime(path zifuchuan) z64 kecuo修改时间(Unix 秒)
shanchu_wenjianremove_file(path zifuchuan) zhenjia kecuo删除文件
chuangjian_mulucreate_dir_all(path zifuchuan) zhenjia kecuo创建目录,包括所有上级目录
shanchu_muluremove_dir_all(path zifuchuan) zhenjia kecuo删除整个目录树
du_muluread_dir(path zifuchuan) [zifuchuan] kecuo列出目录内容
chongmingmingrename(src zifuchuan, dst zifuchuan) zhenjia kecuo重命名或移动
cpro
changshi {
    ruguo (!wenjian.cunzai("cache")) {
        wenjian.chuangjian_mulu("cache/objects")
    }
    bl names = wenjian.du_mulu("cache")
    jishu (name shuyu names) {
        dayin(name)
    }
} buhuo (e) {
    dayin(e.xinxi)
}
cpro
try {
    if (!fs.exists("cache")) {
        fs.create_dir_all("cache/objects")
    }
    var names = fs.read_dir("cache")
    for (name in names) {
        print(name)
    }
} catch (e) {
    print(e.msg)
}
  • cunzai、shi_mulu 不会失败:不存在、没有权限等情况一律返回 jia
  • chuangjian_mulu 在目录已存在时视为成功
  • shanchu_mulu 会递归删除整个目录,不可撤销;遇到符号链接只删链接本身,不会删链接指向的内容
  • du_mulu 只列一层,只返回名字(不含路径),不保证顺序
  • 路径原样交给系统,不会展开 ~,也不会帮你拼接路径

边读边写:文件句柄 ​

文件很大、或者要边读边处理时,打开一个句柄:

拼音英文签名
dakaiopen(path zifuchuan, mode zifuchuan = "r") 句柄 kecuo
du_hangread_line(f &句柄) kexuan(zifuchuan)
xiewrite(f &句柄, s zifuchuan) z64 kecuo
guanbiclose(f &句柄) zhenjia kecuo

打开模式:

mode含义
"r"只读,文件必须存在(默认)
"w"只写,创建或清空
"a"只写,创建或追加到末尾
cpro
daoru "wenjian"

rukou() {
    changshi {
        bl f = wenjian.dakai("big.log")
        bl line = wenjian.du_hang(&f)
        dang (line != kong) {
            dayin(jiebao(line, ""))
            line = wenjian.du_hang(&f)
        }
    } buhuo (e) {
        dayin(e.xinxi)
    }
}
cpro
import "fs"

rukou() {
    try {
        var f = fs.open("big.log")
        var line = fs.read_line(&f)
        while (line != none) {
            print(unwrap(line, ""))
            line = fs.read_line(&f)
        }
    } catch (e) {
        print(e.msg)
    }
}
  • du_hang 返回的内容不含换行符,读到末尾返回 kong;读取出错也返回 kong
  • xie 不自动加换行,返回实际写入的字节数
  • 句柄函数都用借用 &f 传入

句柄会自动关闭 ​

句柄是 nocopy 结构体,所在代码块结束时自动关闭,不会泄漏。因此它也有这些限制:

  • 不能复制、不能移动,只能作为局部变量,用 &f 借给别的函数
  • 句柄的类型名在你的代码里用不了,只能用变量接住 dakai 的返回值
  • 需要处理句柄的辅助函数,请传路径给它,由它自己 dakai

提前关闭用 guanbi(&f),不要用 xiaohui。

重要数据要显式关闭 ​

自动关闭只保证不泄漏,关闭时的错误会被忽略。而写入成功不代表数据已经落盘,磁盘满之类的错误常常到关闭时才出现。写重要数据时请显式关闭:

cpro
changshi {
    bl f = wenjian.dakai("report.csv", "w")
    wenjian.xie(&f, "a,b,c\n")
    wenjian.guanbi(&f)      // 关闭失败会在这里报错
} buhuo (e) {
    dayin("写入失败:", e.xinxi)
}
cpro
try {
    var f = fs.open("report.csv", "w")
    fs.write(&f, "a,b,c\n")
    fs.close(&f)            // 关闭失败会在这里报错
} catch (e) {
    print("写入失败:", e.msg)
}

guanbi 可以重复调用,作用域结束时不会再关一次。关闭后再用这个句柄读写,会在运行时返回错误。

错误码 ​

错误码含义例子
0其他错误—
1文件或目录不存在读取不存在的文件
2权限不足读取没有权限的文件
3需要文件,但它是目录往一个已存在的目录名里写文本
4路径中间某一段不是目录a.txt/b.txt,而 a.txt 是文件
5已存在—
6目录非空重命名的目标是非空目录
7磁盘空间不足—
8读写设备出错—
9内存不足—

请用判断函数,不要直接写数字。判断函数接收的是 e.daima:

拼音英文判断
shi_bu_cunzaiis_not_found不存在(1)
shi_wu_quanxianis_permission_denied权限不足(2)
shi_yi_cunzaiis_exists已存在(5)

Linux 和 Windows 上的错误码一致,只有一处例外:删除只读文件在 Linux 上成功,在 Windows 上返回权限不足(2)。这是两个系统本身的规则不同,cpro 不做抹平。

不支持 ​

功能替代写法
判断是否为普通文件cunzai(p) && !shi_mulu(p)
复制文件du_zijie 加 xie_zijie
递归列出目录du_mulu 加 shi_mulu 自己递归
定位读写(seek)、截断文件暂不支持
修改权限、符号链接操作通过 调用 C 库 实现

cpro 目前处于测试阶段