即使阅读了标准文档,我仍然无法理解Ruby的Array#pack
和String#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”。他们每个人都以不同的方式“失败”(因为我可能期待错误的事情,所以不是真的失败)。所以有两个问题:
答案 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)