2012-12-05 69 views
7

即使阅读标准文档后,我仍然不明白Ruby的Array#packString#unpack是如何正确工作的。下面是这是造成我最麻烦的例子:我预计这两种操作返回相同的输出Ruby的包装和解压缩说明

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时?
+0

对于''H''格式,''不期望的方式行事根据文件。其他格式的字符似乎行为正确,所以我怀疑这是Ruby使用“H *”的错误。 –

回答

10

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

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

可以使用扭转它:

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

这对大量输入是否有效? –

+0

应该非常高效。我看到的唯一增加的成本是创建临时格式字符串,而'H *'将不得不做任何事情。 –

+0

“H2 *”不起作用很有趣。 –

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

我预计这两种操作返回相同的输出:“ABC”。

理解为什么你的方法没有工作的最简单方法,就是简单地用你期待什么开始:

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

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

所以,似乎红宝石期待您的十六进制字节在一个长字符串而不是数组的单独元素。所以,最简单的答案,你原来的问题是这样的:

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) 
3

Array#pack'H'字符串指令说,数组的内容应该是解释为十六进制字符串的半字节。

在您所提供的第一个例子:

irb(main):002:0> chars.pack("H*") 
=> "a" 

你告诉收拾数组的第一个元素,就好像它是一个十六进制字符串的半字节(半字节)的序列:0x61在这种情况下对应于'a' ASCII字符。

在第二个例子:

irb(main):003:0> chars.pack("HHH") 
=> "```" 

你告诉包装阵列的3个元素,好像他们是半字节(在此情况下,高的部分):0x60对应于'`' ASCII字符。由于缺少“aTemplateString”的“2”或“*”修饰符,低位部分或第二位半字节(0x01)会“丢失”。

你需要的是:

chars.pack('H*' * chars.size) 

为了收拾阵列中的所有元素,就好像它们是十六进制字符串的所有半字节。

'H2' * char.size的情况下,如果数组元素只代表1个字节的十六进制字符串,那么这种情况才能正常工作。

这意味着,像chars = ["6161", "6262", "6363"]将是不完整的:

2.1.5 :047 > chars = ["6161", "6262", "6363"] 
=> ["6161", "6262", "6363"] 
2.1.5 :048 > chars.pack('H2' * chars.size) 
=> "abc" 

同时:

2.1.5 :049 > chars.pack('H*' * chars.size) 
=> "aabbcc"