Lua 文件 I/O
Lua - 文件 I/O
Section titled “Lua - 文件 I/O”Lua 的 I/O(输入/输出)库提供了用于读取和写入文件的函数。它提供了两种主要的文件操作模型:隐式文件描述符(使用默认的输入/输出文件)和显式文件描述符(使用特定的文件对象)。
对于下面的示例,我们假设有一个名为 data.txt 的示例文件,其内容如下:
Hello Lua Learner!This is the second line.End of file.在运行写入该文件的示例之前,请确保您已备份文件,或同意对其进行修改/覆盖。
打开和关闭文件
Section titled “打开和关闭文件”打开文件主要使用的函数是 io.open(filename [, mode])。
filename:要打开的文件名(或路径)。mode:指定文件访问模式的字符串。如果省略,默认模式取决于 Lua 版本和操作系统,但通常是只读的。
常见的文件模式:
| 模式 | 描述 |
|---|---|
"r" | 读取模式(默认)。文件必须存在。从文件开头开始读取。 |
"w" | 写入模式。创建新文件,或将现有文件截断为零长度。从文件开头开始写入。 |
"a" | 追加模式。如果文件不存在则创建新文件。写入总是从文件末尾开始。 |
"r+" | 更新模式(读写)。文件必须存在。读写可以在任何位置进行。 |
"w+" | 更新模式(读写)。创建新文件,或将现有文件截断为零长度。读写可以在任何位置进行。 |
"a+" | 追加更新模式(读写)。如果文件不存在则创建新文件。读取可以在任何位置进行,写入总是追加到文件末尾。 |
你也可以在模式字符串后附加 "b"(例如 "rb"、"wb")来以二进制模式打开文件。这在某些系统(如 Windows)上很重要,可以防止换行符转换。在类 Unix 系统上,二进制模式和文本模式通常是一样的。
如果 io.open 成功,它会返回一个文件对象(也称为文件句柄)。如果失败(例如在 "r" 模式下文件不存在,权限错误),它会返回 nil,后跟一个错误消息和错误代码。
local file, err = io.open("non_existent_file.txt", "r")if not file then print("Error opening file: ", err)else -- 处理文件 file:close()end完成文件操作后,务必使用 file:close() 关闭文件,以释放系统资源并确保数据被刷新到磁盘。
隐式文件描述符(标准 I/O)
Section titled “隐式文件描述符(标准 I/O)”Lua 为标准输入 (io.stdin)、标准输出 (io.stdout) 和标准错误 (io.stderr) 提供了默认的文件句柄。像 io.read()、io.write() 和 print() 这样的函数默认使用这些句柄。
io.input(file_or_filename) 和 io.output(file_or_filename) 可以改变默认的输入/输出文件。
-- 如果 data.txt 不存在,则创建并写入一些内容local initial_file_setup, err_setup = io.open("data.txt", "w")if initial_file_setup then initial_file_setup:write("Hello Lua Learner!\nThis is the second line.\nEnd of file.\n") initial_file_setup:close()else print("Setup error: ", err_setup) return -- 如果设置失败则退出end
-- 使用隐式文件描述符的示例local original_input = io.input() -- 保存原始默认输入local original_output = io.output() -- 保存原始默认输出
-- 将默认输入设置为 data.txtlocal file_in, err_in = io.input("data.txt")if not file_in then print("Error setting input: ", err_in)else local first_line = io.read("*l") -- 从默认输入(现在是 data.txt)读取一行 print("读取的第一行:", first_line) io.close(file_in) -- 关闭与默认输入关联的文件end
-- 恢复原始默认输入io.input(original_input)
-- 使用默认输出追加到 data.txtlocal file_out, err_out = io.output("data.txt", "a") -- io.output 使用模式 'a' 进行追加不符合标准用法,通常它会以写入模式打开 -- 最好使用显式文件句柄进行追加-- 为了安全和清晰,这里我们使用显式文件句柄进行追加:file_out, err_out = io.open("data.txt", "a")if not file_out then print("Error opening for append: ", err_out)else file_out:write("\nAppended via explicit handle.") file_out:close()end
-- 验证追加local verify_file, err_verify = io.open("data.txt", "r")if verify_file then print("\n--- 追加后 data.txt 的内容 ---") print(verify_file:read("*a")) verify_file:close()else print("Error verifying: ", err_verify)end
-- 注意:改变默认输入/输出不如使用显式文件句柄常见。file:read(...) 方法(用于显式文件对象)或 io.read(...)(用于默认输入)用于读取数据。它接受一个或多个格式字符串:
| 格式 | 描述 |
|---|---|
"*n" 或 "n" | 读取一个数字。在尝试读取之前跳过空格。如果在 EOF(文件结束)或无法解析为数字时返回 nil。 |
"*a" 或 "a" | 从当前位置读取整个文件。在 EOF 时返回空字符串。 |
"*l" 或 "l" | 读取下一行,丢弃换行符。在 EOF 时返回 nil。 |
"*L" 或 "L" | 读取下一行,保留换行符。在 EOF 时返回 nil。 |
| 数字 | 读取最多 数字 个字节(一个包含该数量字符的字符串)。在 EOF 时返回 nil。 |
local file, err = io.open("data.txt", "r")if file then print("\n--- 逐行读取 data.txt ---") local line1 = file:read("*l") print("第 1 行:", line1)
local rest_of_file = file:read("*a") print("文件剩余部分:", rest_of_file)
file:close()else print("Error reading file: ", err)endfile:lines() 迭代器对于逐行读取文件非常方便:
print("\n--- 使用 file:lines() 读取 data.txt ---")local file_lines, err_lines = io.open("data.txt", "r")if file_lines then for line in file_lines:lines() do print("迭代器行:", line) end file_lines:close() -- 当传递文件对象时,lines() 迭代器不会自动关闭文件else print("Error opening for lines: ", err_lines)end
-- io.lines(filename) 会自动打开、迭代和关闭文件print("\n--- 使用 io.lines(filename) 读取 data.txt ---")for line in io.lines("data.txt") do print("io.lines 行:", line)endfile:write(...) 方法(或用于默认输出的 io.write(...))将其参数写入文件。参数必须是字符串或数字(数字会被转换为字符串)。它不会自动添加换行符或分隔符。
local file, err = io.open("output.txt", "w") -- 以写入模式打开,如果文件存在则覆盖if file then file:write("Hello, Lua file output!\n") file:write("This is line 2.", " And this is part of line 2.\n") local num = 123 file:write("A number: ", num, "\n") print("数据已写入 output.txt") file:close()else print("Error writing to file: ", err)end其他文件操作
Section titled “其他文件操作”file:seek(whence, offset): 设置文件位置。whence可以是"set"(从头开始)、"cur"(从当前位置开始)或"end"(从末尾开始)。offset是一个字节计数。file:seek()返回当前位置。file:seek("cur", 0)获取当前位置。file:flush(): 将所有缓冲写入刷新到文件。io.type(obj): 如果obj是一个已打开的文件句柄,则返回"file";如果是已关闭的文件句柄,则返回"closed file";否则返回nil。io.tmpfile(): 返回一个临时文件的句柄,以更新模式 ("w+b") 打开。该文件在程序退出或句柄关闭时会自动删除。
使用 seek 的示例:
local file, err = io.open("data.txt", "r")if file then file:seek("set", 6) -- 查找定位到第 7 个字节(索引从 0 开始) local partial_read = file:read(5) -- 读取 5 个字节 print("\n查找定位后读取:'", partial_read, "'") -- 应该是 "Lua L"
local current_pos = file:seek("cur", 0) print("当前位置:", current_pos)
file:seek("end", -12) -- 从末尾查找定位 12 个字节 local end_data = file:read("*a") print("读取最后一部分:'", end_data, "'")
file:close()else print("Error in seek example: ", err)end错误处理: 务必检查 I/O 操作(特别是 io.open、file:read 和 file:write)的返回值,以优雅地处理潜在错误。
实际应用: 文件 I/O 对于读取配置文件、写入日志文件、处理数据集以及保存/加载应用程序状态至关重要。