12

Array#pack標準のドキュメントを読んでも、Ruby のと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」を返すと予想していました。それらのそれぞれは、異なる方法で「失敗」します(おそらく間違ったことを期待しているので、実際には失敗ではありません)。2つの質問:

  1. これらの出力の背後にあるロジックは何ですか?
  2. 16 進数のシーケンスを対応する文字列に変換するなど、必要な効果を得るにはどうすればよいですか。さらに良いことに、整数 n が与えられた場合、それをテキスト ファイルと同じ文字列に変換する方法を教えてください。
4

4 に答える 4

12

今朝も同様の問題に取り組んでいました。配列のサイズが不明な場合は、次を使用できます。

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

次を使用して元に戻すことができます。

str = "abc"
str.unpack('H2' * str.size)
=> ["61", "62", "63"]
于 2012-12-05T18:41:42.247 に答える
5

このArray#pack方法はかなり難解です。質問(2)に対処するために、次のようにすることで、あなたの例を機能させることができました。

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

同様の例については、 Rubyのドキュメントを参照してください。これを行うためのより一般的な方法は次のとおりです。

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

これはおそらく、問題に取り組むための最も効率的な方法ではありません。もっと良い方法があると思いますが、どのような入力から始めているのかを知ることは役に立ちます。

この#packメソッドは、Perlなどの他の言語に共通です。Rubyのドキュメントが役に立たない場合は、他の場所にある類似のドキュメントを参照してください。

于 2012-12-05T18:11:22.067 に答える
4

これらの操作はどちらも同じ出力「abc」を返すと予想していました。

なぜあなたのアプローチがうまくいかなかったのかを理解する最も簡単な方法は、あなたが期待していることから始めることです:

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

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

したがって、Ruby は配列の個別の要素ではなく、1 つの長い文字列で 16 進バイトを想定しているようです。したがって、元の質問に対する最も簡単な答えは次のとおりです。

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)
于 2014-10-01T09:00:55.860 に答える