如何将二进制读入数组

How to read a binary into an array

假设我有一个 90 兆字节的文件。它没有加密,但它是二进制的。

我想将此文件作为字节值数组存储到 table 中,以便我可以逐字节处理文件。

我最多可以腾出 2 GB 的内存,所以记下已处理的字节、尚未处理的字节以及已处理的字节之类的东西就可以了。我不太关心处理需要多长时间。

我该如何处理?

要将字符串 s 的内容拆分为字节数组,请使用 {s:byte(1,-1)}

注意 由于 Egor 的评论,我扩展并重写了这个答案。

您首先需要以二进制模式打开文件。这种区别在 Windows 上很重要,其中默认文本模式会将行尾从 CR+LF 更改为 C 换行符。您可以通过为 "rb".

io.open 指定模式参数来执行此操作

虽然您可以一次一个字节地读取文件,但实际上您会希望在缓冲区中处理文件。这些缓冲区可能相当大,但除非您知道您在一次性脚本中只处理小文件,否则您应该避免使用 file:read"*a" 将整个文件读入缓冲区,因为这会导致非常大的文件出现各种问题.

以二进制模式打开文件后,您可以使用 buffer = file:read(n) 读取其中的一个块,其中 n 是块中字节的整数计数。使用中等大小的 2 的幂可能是最有效的。 return 值要么是 nil,要么是最多 n 字节的字符串。如果长度小于 n 字节,则这是文件中的最后一个缓冲区。 (但是,如果从套接字、管道或终端读取,读取小于 n 可能仅表示尚未有数据到达,这取决于许多其他因素,需要在这句话中进行复杂的解释。)

buffer 中的字符串可以用多种方式处理。只要 #buffer 不是太大,那么 {buffer:byte(1,-1)} 将为缓冲区中的每个字节 return 一个整数字节值数组。太大部分取决于您的 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)

运行 Lua 5.1.4 在我的 Windows 框中使用示例作为其输入,它报告:

length: 402
sum:    30374
mean:   75.557213930348