假设我有一个90兆字节的文件。它不是加密的,但它是二进制的。
我想把这个文件作为字节值的数组存储到一个表中,这样我就可以逐字节处理这个文件。
我最多可以腾出2 GB的内存,所以记下已经处理了哪些字节,哪些字节还没有处理,以及处理过的字节,这些都很好。我不太在乎处理可能需要多长时间。
我应该如何处理?
注意由于Egor的评论,我已经扩展并重写了这个答案。
您首先需要以二进制模式打开文件。这一区别在Windows上很重要,默认文本模式将把换行符从CR+LF改为C换行。您可以通过为"rb"
的io.open
指定模式参数来完成此操作。
尽管您可以一次读取一个字节的文件,但在实践中,您将希望在缓冲区中处理该文件。这些缓冲区可能相当大,但除非您知道在一次性脚本中只处理小文件,否则应避免使用file:read"*a"
将整个文件读取到缓冲区中,因为这会导致非常大的文件出现各种问题。
一旦文件以二进制模式打开,就可以使用buffer = file:read(n)
读取其中的一个块,其中n
是块中字节的整数计数。使用中等大小的二次方可能是最有效的。返回值要么是nil
,要么是一个最多包含n
字节的字符串。如果长度小于n
字节,则这是文件中的最后一个缓冲区。(然而,如果从插座、管道或终端读取的数据小于n
,则可能只表明尚未到达数据,这取决于本句中需要复杂解释的许多其他因素。)
buffer
中的字符串可以通过多种方式进行处理。只要#buffer
不太大,那么{buffer:byte(1,-1)}
将为缓冲区中的每个字节返回一个整数字节值数组。太大在一定程度上取决于构建Lua副本时是如何配置的,也可能取决于其他因素,如可用内存。#buffer > 1E6
当然太大了。在下面的示例中,我使用buffer:byte(i)
一次访问一个字节。这适用于任何大小的缓冲区,至少只要i
保持为整数。
最后,不要忘记关闭文件。
这里有一个完整的例子,经过了轻微的测试。它一次读取一个缓冲区中的文件,并累加总大小和所有字节的总和。然后打印大小、总和和平均字节值。
-- sum all bytes in a file
local name = ...
assert(name, "Usage: "..arg[0].." filename")
file = assert(io.open(name, "rb"))
local sum, len = 0,0
repeat
local buffer = file:read(1024)
if buffer then
len = len + #buffer
for i = 1, #buffer do
sum = sum + buffer:byte(i)
end
end
until not buffer
file:close()
print("length:",len)
print("sum:",sum)
print("mean:", sum / len)
在我的Windows盒子上运行Lua 5.1.4,使用该示例作为输入,它报告:
长度:402总数:30374平均值:75.557213930348
要将字符串s
的内容拆分为字节数组,请使用{s:byte(1,-1)}
。