【问题标题】:Read byte data from a file into multiple integers将文件中的字节数据读入多个整数
【发布时间】:2014-01-02 21:08:54
【问题描述】:

我正在尝试从包含 4 字节整数的连续字节值的文件中读取数据。例如,整数 123 将存储在包含字节的文件中:

00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000010 00000000 00000000 00000000 00000011 

我想阅读这篇文章并将每个数字分配给不同的变量,例如a = 1b = 2c = 3。我该怎么做?

任何有关如何使用readunpack 命令的帮助将不胜感激。此外,如果在您提供的解决方案中,您可以非常简要地解释您的代码为何有效。

此文件由 Java 编写的程序生成。我正在转储字节,因为速度是关键,但如果通过添加分隔符字节或类似内容来更轻松地读取单独的整数,我也愿意接受这个建议。

【问题讨论】:

  • 我的回答有帮助吗?

标签: ruby file binary integer


【解决方案1】:

我推荐使用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 格式,它代表“整数、32 位无符号、网络(大端)字节顺序”(参见Ruby Documentation)。通过使用*,您告诉Ruby 将字节转换为指定的类型,直到数据用完。以下是您将如何使用它:

io = File.open('/path/to/binary/file')
a, b, c = io.read(12).unpack('N*')  #=> 1, 2, 3

如果您需要阅读更多内容,请相应地将参数调整为read(这里3*4 = 12字节)。

【讨论】:

  • 啊 - 我忘了提 - 我想尝试在不使用 gem 的情况下做到这一点。原因是我需要使用专有的 Ruby API,而且它不支持 gems。
  • @PratikThaker 查看我对如何使用内置方法 unpack 的编辑。
  • 顺便说一句,如果你可以运行 ruby​​ 代码,你就可以运行 gems。如果您可以从中受益,也许将 gem 提供给您的项目是一种选择。
【解决方案2】:

您可以使用特殊的字符串运算符从二进制中计算一个数字。您的文件包含以下内容:

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

【讨论】:

  • 嗯 - 这很有趣。那么这些是使用字符串运算符吗?我想知道,将这些数字存储为字符串而不是字节值实际上是否更容易(因此实际上存储“2”而不是 00000010)?就像我说的那样,该文件是由我控制的程序创建的,因此我可以将其更改为以任何我希望的格式输出数字。我唯一的标准是它需要尽可能快。
  • 您应该测量所有建议方法的时间。但是使用 ruby​​ 的主要原因不是速度,而是可用性和代码的简单性。为了快速编写代码,人们使用 c,或者例如类似模式的语言(参见 STALin)。
  • 是二进制码吗?为什么你存储“2”而不是 00000010?
  • 所以这是实际问题:我正在尝试为具有 Ruby API 的软件 (Google SketchUp) 创建一个应用程序。现在我想连接一个没有附加 Ruby API 的控制器(Leap Motion)。所以我正在做的是获取控制器生成的数据,将其输出到文件中,然后通过 Ruby 将其读入 SketchUp。问题是我每隔几毫秒生成一个数据集,因此它需要在两者之间有效流动。
  • 有很多用c写的ruby app。试试我们的建议,如果不满意,请看文章:cs.auckland.ac.nz/references/ruby/ProgrammingRuby/ext_ruby.html
【解决方案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

当您以“1 2 3”这样的格式存储数字时,您的代码应该更快,因为(至于我的解决方案)您不需要使用 gsub 也不需要计算数字在哪里。
不过,我建议您对从本主题获得的代码进行基准测试。 如果你的目标是速度,你可以尝试用 C 扩展你的代码。

这里是红宝石解决方案:

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}

【讨论】:

  • 我正在复制并粘贴我对上面帖子的回复:那么这些是使用字符串运算符吗?我想知道,将这些数字存储为字符串而不是字节值实际上是否更容易(因此实际上存储“2”而不是 00000010)?就像我说的那样,该文件是由我控制的程序创建的,因此我可以将其更改为以任何我希望的格式输出数字。我唯一的标准是它需要尽可能快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-25
相关资源
最近更新 更多