Ruby - 解压混合类型的数组
Ruby - Unpack array with mixed types
我正在尝试使用 unpack
解码二进制文件。二进制文件具有以下结构:
ABCDEF\tFFFABCDEF\tFFFF....
哪里
ABCDEF -> String of fixed length
\t -> tab character
FFF -> 3 Floats
.... -> repeat thousands of times
我知道当类型都相同或只有数字和固定长度数组时该怎么做,但我在这种情况下很挣扎。例如,如果我有一个浮动列表,我会做
s.unpack('F*')
或者如果我有像
这样的整数和浮点数
[1, 3.4, 5.2, 4, 2.3, 7.8]
我愿意
s.unpack('CF2CF2')
但在这种情况下我有点迷茫。我希望使用带括号的格式字符串,例如“(CF2)*”,但它不起作用。
如果重要的话,我需要使用 Ruby 2.0.0-p247
例子
ary = ["ABCDEF\t", 3.4, 5.6, 9.1, "FEDCBA\t", 2.5, 8.9, 3.1]
s = ary.pack('P7fffP7fff')
然后
s.scan(/.{19}/)
["\xA8lf\xF9\xD4\x7F\x00\x00\x9A\x99Y@33\xB3@\x9A\x99\x11", "A\x80lf\xF9\xD4\x7F\x00\x00\x00\x00 @ff\x0EAff"]
终于
s.scan(/.{19}/).map{ |item| item.unpack('P7fff') }
Error: #<ArgumentError: no associated pointer>
<main>:in `unpack'
<main>:in `block in <main>'
<main>:in `map'
<main>:in `<main>'
处理重复且已知固定大小的混合格式时,通常更容易先拆分字符串,
简单的例子是:
binary.scan(/.{LENGTH_OF_DATA}/).map { |item| item.unpack(FORMAT) }
考虑到您上面的示例,取包含制表符的字符串长度(以字节为单位)加上 3 个浮点数的大小。如果您的字符串字面上是 'ABCDEF\t'
,您将使用 19 的大小(字符串为 7,3 个浮点数为 12)。
您的最终产品将如下所示:
str.scan(/.{19}/).map { |item| item.unpack('P7fff') }
例如:
irb(main):001:0> ary = ["ABCDEF\t", 3.4, 5.6, 9.1, "FEDCBA\t", 2.5, 8.9, 3.1]
=> ["ABCDEF\t", 3.4, 5.6, 9.1, "FEDCBA\t", 2.5, 8.9, 3.1]
irb(main):002:0> s = ary.pack('pfffpfff')
=> "\xE8Pd\xE4eU\x00\x00\x9A\x99Y@33\xB3@\x9A\x99\x11A\x98Pd\xE4eU\x00\x00\x00\x00 @ff\x0EAffF@"
irb(main):003:0> s.unpack('pfffpfff')
=> ["ABCDEF\t", 3.4000000953674316, 5.599999904632568, 9.100000381469727, "FEDCBA\t", 2.5, 8.899999618530273, 3.0999999046325684]
精度上的微小差异是不可避免的,但不要担心,因为它来自 32 位浮点数和 64 位双精度数(Ruby 内部使用的)的差异,以及精度差异将小于 32 位浮点数的显着性。
您可以以 19 字节的小块读取文件并使用 'A7fff'
打包和解包。不要使用指向结构的指针('p'
和 'P'
),因为它们需要超过 19 个字节来编码您的信息。
您还可以使用 'A6xfff'
忽略第 7 个字节并获得包含 6 个字符的字符串。
这里有一个例子,类似于IO.read
的文档:
data = [["ABCDEF\t", 3.4, 5.6, 9.1],
["FEDCBA\t", 2.5, 8.9, 3.1]]
binary_file = 'data.bin'
chunk_size = 19
pattern = 'A7fff'
File.open(binary_file, 'wb') do |o|
data.each do |row|
o.write row.pack(pattern)
end
end
raise "Something went wrong. Please check data, pattern and chunk_size." unless File.size(binary_file) == data.length * chunk_size
File.open(binary_file, 'rb') do |f|
while record = f.read(chunk_size)
puts '%s %g %g %g' % record.unpack(pattern)
end
end
# =>
# ABCDEF 3.4 5.6 9.1
# FEDCBA 2.5 8.9 3.1
如果您的文件很大,您可以使用 19
的倍数来加快处理速度。
我正在尝试使用 unpack
解码二进制文件。二进制文件具有以下结构:
ABCDEF\tFFFABCDEF\tFFFF....
哪里
ABCDEF -> String of fixed length
\t -> tab character
FFF -> 3 Floats
.... -> repeat thousands of times
我知道当类型都相同或只有数字和固定长度数组时该怎么做,但我在这种情况下很挣扎。例如,如果我有一个浮动列表,我会做
s.unpack('F*')
或者如果我有像
这样的整数和浮点数[1, 3.4, 5.2, 4, 2.3, 7.8]
我愿意
s.unpack('CF2CF2')
但在这种情况下我有点迷茫。我希望使用带括号的格式字符串,例如“(CF2)*”,但它不起作用。
如果重要的话,我需要使用 Ruby 2.0.0-p247
例子
ary = ["ABCDEF\t", 3.4, 5.6, 9.1, "FEDCBA\t", 2.5, 8.9, 3.1]
s = ary.pack('P7fffP7fff')
然后
s.scan(/.{19}/)
["\xA8lf\xF9\xD4\x7F\x00\x00\x9A\x99Y@33\xB3@\x9A\x99\x11", "A\x80lf\xF9\xD4\x7F\x00\x00\x00\x00 @ff\x0EAff"]
终于
s.scan(/.{19}/).map{ |item| item.unpack('P7fff') }
Error: #<ArgumentError: no associated pointer>
<main>:in `unpack'
<main>:in `block in <main>'
<main>:in `map'
<main>:in `<main>'
处理重复且已知固定大小的混合格式时,通常更容易先拆分字符串,
简单的例子是:
binary.scan(/.{LENGTH_OF_DATA}/).map { |item| item.unpack(FORMAT) }
考虑到您上面的示例,取包含制表符的字符串长度(以字节为单位)加上 3 个浮点数的大小。如果您的字符串字面上是 'ABCDEF\t'
,您将使用 19 的大小(字符串为 7,3 个浮点数为 12)。
您的最终产品将如下所示:
str.scan(/.{19}/).map { |item| item.unpack('P7fff') }
例如:
irb(main):001:0> ary = ["ABCDEF\t", 3.4, 5.6, 9.1, "FEDCBA\t", 2.5, 8.9, 3.1]
=> ["ABCDEF\t", 3.4, 5.6, 9.1, "FEDCBA\t", 2.5, 8.9, 3.1]
irb(main):002:0> s = ary.pack('pfffpfff')
=> "\xE8Pd\xE4eU\x00\x00\x9A\x99Y@33\xB3@\x9A\x99\x11A\x98Pd\xE4eU\x00\x00\x00\x00 @ff\x0EAffF@"
irb(main):003:0> s.unpack('pfffpfff')
=> ["ABCDEF\t", 3.4000000953674316, 5.599999904632568, 9.100000381469727, "FEDCBA\t", 2.5, 8.899999618530273, 3.0999999046325684]
精度上的微小差异是不可避免的,但不要担心,因为它来自 32 位浮点数和 64 位双精度数(Ruby 内部使用的)的差异,以及精度差异将小于 32 位浮点数的显着性。
您可以以 19 字节的小块读取文件并使用 'A7fff'
打包和解包。不要使用指向结构的指针('p'
和 'P'
),因为它们需要超过 19 个字节来编码您的信息。
您还可以使用 'A6xfff'
忽略第 7 个字节并获得包含 6 个字符的字符串。
这里有一个例子,类似于IO.read
的文档:
data = [["ABCDEF\t", 3.4, 5.6, 9.1],
["FEDCBA\t", 2.5, 8.9, 3.1]]
binary_file = 'data.bin'
chunk_size = 19
pattern = 'A7fff'
File.open(binary_file, 'wb') do |o|
data.each do |row|
o.write row.pack(pattern)
end
end
raise "Something went wrong. Please check data, pattern and chunk_size." unless File.size(binary_file) == data.length * chunk_size
File.open(binary_file, 'rb') do |f|
while record = f.read(chunk_size)
puts '%s %g %g %g' % record.unpack(pattern)
end
end
# =>
# ABCDEF 3.4 5.6 9.1
# FEDCBA 2.5 8.9 3.1
如果您的文件很大,您可以使用 19
的倍数来加快处理速度。