文件(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_wenben | read_to_string | (path zifuchuan) zifuchuan kecuo |
xie_wenben | write_string | (path zifuchuan, content zifuchuan) zhenjia kecuo |
zhuijia_wenben | append_string | (path zifuchuan, content zifuchuan) zhenjia kecuo |
yuanzi_xie_wenben | write_string_atomic | (path zifuchuan, content zifuchuan) zhenjia kecuo |
du_zijie | read_bytes | (path zifuchuan) [w8] kecuo |
xie_zijie | write_bytes | (path zifuchuan, data [w8]) zhenjia kecuo |
xie_wenben是覆盖写:文件不存在就创建,存在就清空后再写- 内容原样读写,不转换换行符
du_wenben要求文件内容是合法 UTF-8;读二进制文件请用du_zijie- 返回
zhenjia的函数成功时总是zheng,失败会进入buhuo,不用检查返回值
原子写
yuanzi_xie_wenben 先写到同目录下的临时文件,写完再改名替换目标文件。中途出错时目标文件保持原样,任何时候读到的要么是旧内容、要么是完整的新内容。
配置文件、索引这类不能只写一半的文件,请用它。代价是多一次写入和改名,所以不是默认行为。
文件信息与目录
| 拼音 | 英文 | 签名 | 说明 |
|---|---|---|---|
cunzai | exists | (path zifuchuan) zhenjia | 是否存在 |
shi_mulu | is_dir | (path zifuchuan) zhenjia | 是否为目录 |
wenjian_daxiao | file_size | (path zifuchuan) z64 kecuo | 文件大小(字节) |
xiugai_shijian | mtime | (path zifuchuan) z64 kecuo | 修改时间(Unix 秒) |
shanchu_wenjian | remove_file | (path zifuchuan) zhenjia kecuo | 删除文件 |
chuangjian_mulu | create_dir_all | (path zifuchuan) zhenjia kecuo | 创建目录,包括所有上级目录 |
shanchu_mulu | remove_dir_all | (path zifuchuan) zhenjia kecuo | 删除整个目录树 |
du_mulu | read_dir | (path zifuchuan) [zifuchuan] kecuo | 列出目录内容 |
chongmingming | rename | (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不会失败:不存在、没有权限等情况一律返回jiachuangjian_mulu在目录已存在时视为成功shanchu_mulu会递归删除整个目录,不可撤销;遇到符号链接只删链接本身,不会删链接指向的内容du_mulu只列一层,只返回名字(不含路径),不保证顺序- 路径原样交给系统,不会展开
~,也不会帮你拼接路径
边读边写:文件句柄
文件很大、或者要边读边处理时,打开一个句柄:
| 拼音 | 英文 | 签名 |
|---|---|---|
dakai | open | (path zifuchuan, mode zifuchuan = "r") 句柄 kecuo |
du_hang | read_line | (f &句柄) kexuan(zifuchuan) |
xie | write | (f &句柄, s zifuchuan) z64 kecuo |
guanbi | close | (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;读取出错也返回kongxie不自动加换行,返回实际写入的字节数- 句柄函数都用借用
&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_cunzai | is_not_found | 不存在(1) |
shi_wu_quanxian | is_permission_denied | 权限不足(2) |
shi_yi_cunzai | is_exists | 已存在(5) |
Linux 和 Windows 上的错误码一致,只有一处例外:删除只读文件在 Linux 上成功,在 Windows 上返回权限不足(2)。这是两个系统本身的规则不同,cpro 不做抹平。
不支持
| 功能 | 替代写法 |
|---|---|
| 判断是否为普通文件 | cunzai(p) && !shi_mulu(p) |
| 复制文件 | du_zijie 加 xie_zijie |
| 递归列出目录 | du_mulu 加 shi_mulu 自己递归 |
| 定位读写(seek)、截断文件 | 暂不支持 |
| 修改权限、符号链接操作 | 通过 调用 C 库 实现 |
