21.2.2 二进制文件 |
默认的简单模式总是以文本模式打开。在Unix中二进制文件和文本文件并没有区别,但是在如Windows这样的系统中,二进制文件必须以显式的标记来打开文件。控制这样的二进制文件,你必须将“b”标记添加在io.open函数的格式字符串参数中。在Lua中二进制文件的控制和文本类似。一个串可以包含任何字节值,库中几乎所有的函数都可以用来处理任意字节值。(你甚至可以对二进制的“串”进行模式比较,只要串中不存在0值。如果想要进行0值字节的匹配,你可以使用%z代替)这样使用*all模式就是读取整个文件的值,使用数字n就是读取n个字节的值。以下是一个将文本文件从DOS模式转换到Unix模式的简单程序。(这样转换过程就是将“回车换行字符”替换成“换行字符”。)因为是以二进制形式(原稿是Text Mode!!??)打开这些文件的,这里无法使用标准输入输入文件(stdin/stdout)。所以使用程序中提供的参数来得到输入、输出文件名。
local inp = assert(io.open(arg[1], "rb"))
local out = assert(io.open(arg[2], "wb"))
local data = inp:read("*all")
data = string.gsub(data, "\r\n", "\n")
out:write(data)
assert(out:close())
可以使用如下的命令行来调用该程序。
> lua prog.lua file.dos file.unix
第二个例子程序:打印在二进制文件中找到的所有特定字符串。该程序定义了一种最少拥有六个“有效字符”,以零字节值结尾的特定串。(本程序中“有效字符”定义为文本数字、标点符号和空格符,由变量validchars定义。)在程序中我们使用连接和string.rep函数创建validchars,以%z结尾来匹配串的零结尾。
local f = assert(io.open(arg[1], "rb"))
local data = f:read("*all")
local validchars = "[%w%p%s]"
local pattern = string.rep(validchars, 6) .. "+%z"
for w in string.gfind(data, pattern) do
print(w)
end
最后一个例子:该程序对二进制文件进行一次值分析[6](Dump)。程序的第一个参数是输入文件名,输出为标准输出。其按照10字节为一段读取文件,将每一段各字节的十六进制表示显示出来。接着再以文本的形式写出该段,并将控制字符转换为点号。
local f = assert(io.open(arg[1], "rb"))
local block = 10
while true do
local bytes = f:read(block)
if not bytes then break end
for b in string.gfind(bytes, ".") do
io.write(string.format("%02X ", string.byte(b)))
end
io.write(string.rep(" ", block - string.len(bytes) + 1))
io.write(string.gsub(bytes, "%c", "."), "\n")
end
如果以vip来命名该程序脚本文件。可以使用如下命令来执行该程序处理其自身:
prompt> lua vip vip
在Unix系统中它将会会产生一个如下的输出样式:
6C 6F 63 61 6C 20 66 20 3D 20 local f =
61 73 73 65 72 74 28 69 6F 2E assert(io.
6F 70 65 6E 28 61 72 67 5B 31 open(arg[1
5D 2C 20 22 72 62 22 29 29 0A ], "rb")).
...
22 25 63 22 2C 20 22 2E 22 29 "%c", ".")
2C 20 22 5C 6E 22 29 0A 65 6E , "\n").en
64 0A d.