第七章:I/O 操作

任何有用的程序都需要与外部世界交互。Lua 的 I/O 库提供了两种模型来处理文件输入输出:简单 I/O 模型和完整 I/O 模型。

  • 简单模型:假设有一个当前输入文件和一个当前输出文件,所有的 I/O 操作都针对这两个文件。

  • 完整模型:使用文件句柄(File Handles),类似于 C 语言中的 FILE*,可以同时操作多个文件。

7.1 简单 I/O 模型

简单模型非常适合处理标准输入(stdin)和标准输出(stdout),或者简单的文件读写任务。

io.input 和 io.output

  • io.input(filename): 设置默认输入文件。如果不带参数,返回当前输入文件句柄。

  • io.output(filename): 设置默认输出文件。

io.read

从默认输入文件中读取数据。参数决定了读取的方式:

  • "a": 读取整个文件。

  • "l": 读取下一行(丢弃换行符)。这是默认值。

  • "L": 读取下一行(保留换行符)。

  • n: 读取指定数量的字符。

  • "n": 读取一个数字。

-- 读取用户输入的一行
local line = io.read()

-- 读取一个数字
local num = io.read("n")

io.write

向默认输出文件写入数据。可以接受任意数量的字符串或数字参数。

io.write("Hello", " ", "World", "\n")

示例:复制文件

io.input("input.txt")
io.output("output.txt")

local content = io.read("a")
io.write(content)

7.2 完整 I/O 模型

完整模型基于文件句柄,更加灵活。通过 io.open 打开文件,获得句柄后,使用句柄的方法(如 f:read)进行操作。

io.open

语法:io.open(filename, mode)
mode 模式字符串:

  • "r": 只读模式(默认)。

  • "w": 只写模式(覆盖原文件)。

  • "a": 追加模式。

  • "r+": 更新模式(读写),保留原内容。

  • "w+": 更新模式(读写),清空原内容。

  • "a+": 更新模式(读写),追加内容。

  • "b": 二进制模式(可以与其他模式组合,如 "rb")。

如果打开失败,io.open 返回 nil 和错误信息。

local f, err = io.open("nonexistent.txt", "r")
if not f then
    print("Error:", err)
else
    -- 操作文件
    f:close()
end

文件句柄方法

一旦拥有了文件句柄 f,就可以使用冒号语法调用方法:

  • f:read(...): 与 io.read 类似。

  • f:write(...): 与 io.write 类似。

  • f:seek([whence] [, offset]): 移动文件指针。

    • whence: "set" (开头), "cur" (当前), "end" (结尾)。
    • 返回当前位置。
  • f:close(): 关闭文件。

  • f:flush(): 刷新缓冲区。

示例:按行读取文件

local f = io.open("data.txt", "r")
if f then
    for line in f:lines() do
        print(line)
    end
    f:close()
end

io.lines

io.lines(filename) 返回一个迭代器,每次调用返回文件的一行。文件会在迭代结束后自动关闭。
如果是 f:lines(),则文件不会在迭代结束后自动关闭。

-- 简单方式
for line in io.lines("data.txt") do
    print(line)
end

7.3 其他 I/O 操作

io.tmpfile

返回一个临时文件的句柄,该文件以读写模式打开。程序结束时该文件会自动删除。

io.popen

启动一个系统进程,并返回一个文件句柄用于读取该进程的输出或向其写入输入。

-- 读取 ls 命令的输出 (Linux/Mac)
local f = io.popen("ls -la", "r")
for line in f:lines() do
    print(line)
end
f:close()

练习题

  1. 编写一个程序,读取一个文本文件,统计其中的字符数、单词数和行数(类似于 wc 命令)。

  2. 编写一个程序,将一个文件的内容按行逆序写入另一个文件(即第一行变最后一行)。

  3. 使用 io.popen 获取当前系统的日期和时间。

  4. 尝试以二进制模式读取一张图片文件的前 10 个字节,并以十六进制格式打印出来。


下一章预告:编程难免出错。下一章我们将学习 Lua 的错误处理机制(pcall, xpcall)以及调试技巧。