我正试图从一个包含4字节整数的连续字节值的文件中读取数据。例如,整数1
、2
、3
将存储在包含以下字节的文件中:
00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000010 00000000 00000000 00000000 00000011
我想阅读这篇文章,并将每个数字分配给不同的变量,例如a = 1
、b = 2
和c = 3
。我该怎么做?
对于如何使用read
和unpack
命令的任何帮助,我们将不胜感激。此外,如果在您提供的解决方案中,您可以对代码工作的原因做出非常简短的解释。
这个文件是由一个用Java编写的程序生成的。我之所以转储字节,是因为速度是关键,但如果通过添加分隔符字节或类似的东西,读取单独整数的过程变得更容易,我也会接受这个建议。
我建议使用bindata gem:
require 'bindata'
class MyBinaryFormat < BinData::Record
uint32 :a
uint32 :b
uint32 :c
end
io = File.open('/path/to/binary/file')
result = MyBinaryFormat.read(io)
puts result.a # 1
puts result.b # 2
puts result.c # 3
如果你不能使用宝石,你可以使用String#unpack
。您需要使用N
格式,它代表"Integer,32-bit unsigned,network(big-endian)byte order"(请参阅Ruby文档)。通过使用*
,您可以告诉Ruby将字节转换为指定的类型,直到用完数据为止。以下是如何使用它:
io = File.open('/path/to/binary/file')
a, b, c = io.read(12).unpack('N*') #=> 1, 2, 3
如果您需要阅读更多内容,请相应地将参数调整为read
(此处为3*4=12字节)。
您可以使用特殊的字符串运算符从二进制计算数字。您的文件包含以下内容:
00000000 00000001 00000000 00000010 00000000 00000011
代码如下:
# => ["00000000", "00000001", "00000000", "00000010", "00000000", "00000011"]
values =
IO.read( '1.1' ).split( /s+/ ).map do| binary | # reading the file and splitting into an array by space
i = -1
binary.split( '' ).reverse.reduce( 0 ) do| sum, digit | # reduction binary into a digit
i += 1
sum + ( digit.to_i << i ) # sum by a digit
end
end
=> [0, 1, 0, 2, 0, 3]
对于以下代码,将数组中先前存储的所有值传递到函数proc_func
中,扩展参数:
def proc_func a, b, c, d, e, f
puts a, b, c, d, e, f
end
proc_func *values
# 0
# 1
# 0
# 2
# 0
# 3
以下是不使用unpack
的方法。
将这一行读入字符串(str
)后:
arr = []
str = str.gsub(/s/, '') #delete every space
len = str.length #get length of string
i = 0
while i<len #iterate over string until end(starting from 0)
arr<<str[i...(i+16)].to_i(2) # "unpacking" 16 characters using range: 'string'[0...2] is 'st' & changing it into Integer with base 2(`to_i(base)`)
i += 16 #going to next number(in your case 16 characters later)
end
当你以这样的格式存储数字时,你的代码应该更快,因为(对于我的解决方案)你不需要使用gsub
,也不需要计算数字在哪里
尽管如此,我还是建议您对从本主题中获得的代码进行基准测试。如果你的目标是速度,你可以尝试用C.扩展你的代码
这是ruby解决方案:
str = "1 2 3 4"
arr = str.split #split string on space (it's the same as `str.split(' ')`
#result: ["1", "2", "3", "4"]
numbers = arr.collect{|el| el.to_i} #for each string in `arr` it calls `to_i` and store result in new array(not `arr`)
#[1, 2, 3, 4]
当然,你可以这样做:
numbers = str.split.collect &:to_i
或者像这样:
numbers = str.split.collect(|el| el.to_i}