如何将二进制读入数组
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
假设我有一个 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