Ruby的解包和解包

时间:2012-12-05 17:57:51

标签: ruby pack

即使阅读了标准文档,我仍然无法理解Ruby的Array#packString#unpack是如何工作的。这是导致我最麻烦的例子:

irb(main):001:0> chars = ["61","62","63"]
=> ["61", "62", "63"]
irb(main):002:0> chars.pack("H*")
=> "a"
irb(main):003:0> chars.pack("HHH")
=> "```"

我希望这两个操作都返回相同的输出:“abc”。他们每个人都以不同的方式“失败”(因为我可能期待错误的事情,所以不是真的失败)。所以有两个问题:

  1. 这些产出背后的逻辑是什么?
  2. 如何实现我想要的效果,即将十六进制数字序列转换为相应的字符串。更好的是 - 给定一个整数n,如何将其转换为与文本文件相同的字符串,当被视为数字时(例如,在十六进制编辑器中)等于n?

4 个答案:

答案 0 :(得分:10)

我们今天早上正在处理类似的问题。如果数组大小未知,您可以使用:

ary = ["61", "62", "63"]
ary.pack('H2' * ary.size)
=> "abc"

您可以使用以下方式撤消它:

str = "abc"
str.unpack('H2' * str.size)
=> ["61", "62", "63"]

答案 1 :(得分:5)

Array#pack方法非常神秘。在回答问题(2)时,我能够通过这样做得到你的榜样:

> ["61", "62", "63"].pack("H2H2H2")
=> "abc" 

有关类似示例,请参阅Ruby documentation。这是一种更通用的方法:

["61", "62", "63"].map {|s| [s].pack("H2") }.join

这可能不是解决问题的最有效方法;我怀疑有更好的方法,但它有助于了解你开始的输入类型。

#pack方法对其他语言很常见,例如Perl。如果Ruby的文档没有帮助,您可以在其他地方查阅类似的文档。

答案 2 :(得分:5)

'H'的{​​{1}}字符串指令表示数组内容应该被解释为十六进制字符串的半字节。

在第一个例子中,您提供了:

Array#pack

你要告诉打包数组的第一个元素,好像它是十六进制字符串的半字节(半字节)序列:irb(main):002:0> chars.pack("H*") => "a" 在这种情况下对应{{1} } ASCII字符。

在第二个例子中:

0x61

你要告诉他们将数组中的3个元素打包为好像它们是半字节(在这种情况下是高位):'a'对应于irb(main):003:0> chars.pack("HHH") => "```" ASCII字符。低部分或第二个半字节(0x60)"丢失"由于缺少' 2'或' *' " aTemplateString"。

的修饰符

您需要的是:

'`'

为了打包数组中所有元素的所有半字节,就好像它们是十六进制字符串一样。

如果数组元素仅代表1字节的十六进制字符串,则0x01的情况才能正常工作。

这意味着像chars.pack('H*' * chars.size) 这样的东西将不完整:

'H2' * char.size

while:

chars = ["6161", "6262", "6363"]

答案 3 :(得分:4)

  

我希望这两个操作都返回相同的输出:“abc”。

了解您的方法无效的最简单方法是简单地从您期望的开始:

"abc".unpack("H*")
# => ["616263"]

["616263"].pack("H*")
# => "abc"

因此,似乎Ruby期望你的十六进制字节在一个长字符串中而不是数组的单独元素。因此,对原始问题的最简单答案是:

chars = ["61", "62", "63"]
[chars.join].pack("H*")
# => "abc"

对于大量投入,这种方法似乎表现得相当好:

require 'benchmark'

chars = ["61", "62", "63"] * 100000

Benchmark.bmbm do |bm|
  bm.report("join pack") do [chars.join].pack("H*") end
  bm.report("big pack") do chars.pack("H2" * chars.size) end
  bm.report("map pack") do chars.map{ |s| [s].pack("H2") }.join end
end

#                 user     system      total        real
# join pack   0.030000   0.000000   0.030000 (  0.025558)
# big pack    0.030000   0.000000   0.030000 (  0.027773)
# map pack    0.230000   0.010000   0.240000 (  0.241117)