2012-12-05 68 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"