第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但有一些不同之处。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目。
用 Lua 编程 | ||
第 III 部分。标准库 第 21 章。I/O 库 |
简单模型对其所有操作都在两个当前文件中执行。该库将当前输入文件初始化为进程的标准输入 (stdin
),将当前输出文件初始化为进程的标准输出 (stdout
)。因此,当我们执行类似 io.read()
的操作时,我们从标准输入读取一行。
我们可以使用 io.input
和 io.output
函数更改这些当前文件。类似 io.input(filename)
的调用会打开给定文件(以读取模式),并将其设置为当前输入文件。从这时起,所有输入都将来自此文件,直到再次调用 io.input
;io.output
对输出执行类似的工作。如果出现错误,这两个函数都会引发错误。如果您想直接处理错误,则必须使用完整模型中的 io.open
。
由于 write
比 read
更简单,因此我们先来看一下它。io.write
函数只是获取任意数量的字符串参数,并将它们写入当前输出文件。数字按照通常的转换规则转换为字符串;要完全控制此转换,您应该使用 string
库中的 format
函数。
> io.write("sin (3) = ", math.sin(3), "\n") --> sin (3) = 0.1411200080598672 > io.write(string.format("sin (3) = %.4f\n", math.sin(3))) --> sin (3) = 0.1411避免使用类似
io.write(a..b..c)
的代码;调用 io.write(a,b,c)
可以通过更少的资源实现相同的效果,因为它避免了连接。
通常,您应该将 print
用于快速且不完善的程序或用于调试,将 write
用于需要完全控制输出的情况。
> print("hello", "Lua"); print("Hi") --> hello Lua --> Hi > io.write("hello", "Lua"); io.write("Hi", "\n") --> helloLuaHi与
print
不同,write
不会向输出添加额外的字符,例如制表符或换行符。此外,write
使用当前输出文件,而 print
始终使用标准输出。最后,print
会自动对其参数应用 tostring
,因此它还可以显示表、函数和 nil。
read
函数从当前输入文件读取字符串。其参数控制读取的内容
"*all" | 读取整个文件 |
"*line" | 读取下一行 |
"*number" | 读取一个数字 |
num | 读取一个最多包含 num 个字符的字符串 |
调用 io.read("*all")
会从当前位置开始读取整个当前输入文件。如果我们处于文件末尾,或者文件为空,该调用会返回一个空字符串。
由于 Lua 可以高效地处理长字符串,因此在 Lua 中编写筛选器的简单技术是将整个文件读入字符串,对字符串进行处理(通常使用 gsub
),然后将字符串写入输出
t = io.read("*all") -- read the whole file t = string.gsub(t, ...) -- do the job io.write(t) -- write the file例如,以下代码是一个完整程序,用于使用 MIME 的quoted-printable 编码对文件的文本进行编码。在此编码中,非 ASCII 字符编码为
=
XX,其中 XX 是十六进制中字符的数字代码。为了保持编码的一致性,=
´ 字符也必须编码。gsub
中使用的模式捕获了代码从 128 到 255 的所有字符,以及等号。
t = io.read("*all") t = string.gsub(t, "([\128-\255=])", function (c) return string.format("=%02X", string.byte(c)) end) io.write(t)在奔腾 333MHz 上,此程序需要 0.2 秒来转换一个包含 200K 字符的文件。
调用 io.read("*line")
会从当前输入文件中返回下一行,不带换行符。当我们到达文件末尾时,该调用会返回 nil(因为没有下一行要返回)。此模式是 read
的默认模式,因此 io.read()
的效果与 io.read("*line")
相同。通常,我们仅在算法按行自然处理文件时使用此模式;否则,我们倾向于一次使用 *all
读取整个文件,或分块读取,如我们稍后将看到的。作为使用此模式的一个简单示例,以下程序将其当前输入复制到当前输出,对每一行进行编号
local count = 1 while true do local line = io.read() if line == nil then break end io.write(string.format("%6d ", count), line, "\n") count = count + 1 end但是,要逐行迭代整个文件,我们最好使用
io.lines
迭代器。例如,我们可以编写一个完整程序来对文件的行进行如下排序
local lines = {} -- read the lines in table 'lines' for line in io.lines() do table.insert(lines, line) end -- sort table.sort(lines) -- write all the lines for i, l in ipairs(lines) do io.write(l, "\n") end此程序在 1.8 秒内对一个包含 4.5 MB(32K 行)的文件进行排序(在奔腾 333MHz 上),而系统
sort
程序花费 0.6 秒,该程序是用 C 编写的,并且经过高度优化。
调用 io.read("*number")
会从当前输入文件中读取一个数字。这是 read
返回数字而不是字符串的唯一情况。当您需要从文件中读取许多数字时,中间字符串的缺失可以显著提高性能。*number
选项会跳过数字之前的任何空格,并接受诸如 -3
、+5.2
、1000
和 -3.4e-23
的数字格式。如果它在当前文件位置找不到数字(由于格式错误或文件末尾),它会返回 nil。
您可以使用多个选项调用 read
;对于每个参数,该函数将返回相应的结果。假设您有一个文件中每行包含三个数字
6.0 -3.23 15e12 4.3 234 1000001 ...现在您想打印每行的最大值。您可以在对
read
的单次调用中读取所有三个数字
while true do local n1, n2, n3 = io.read("*number", "*number", "*number") if not n1 then break end print(math.max(n1, n2, n3)) end在任何情况下,您都应考虑使用
io.read
的选项 "*all"
读取整个文件,然后使用 gfind
将其分解的替代方案
local pat = "(%S+)%s+(%S+)%s+(%S+)%s+" for n1, n2, n3 in string.gfind(io.read("*all"), pat) do print(math.max(n1, n2, n3)) end
除了基本的读取模式外,您还可以使用数字 n 作为参数调用 read
:在这种情况下,read
尝试从输入文件中读取 n 个字符。如果它无法读取任何字符(文件结尾),read
返回 nil;否则,它返回一个最多包含 n 个字符的字符串。作为此读取模式的一个示例,以下程序是(当然在 Lua 中)从 stdin
复制文件到 stdout
的一种有效方式
local size = 2^13 -- good buffer size (8K) while true do local block = io.read(size) if not block then break end io.write(block) end
作为特例,io.read(0)
可用作文件结尾的测试:如果还有更多内容要读取,它将返回一个空字符串,否则返回 nil。
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |