pack テンプレート文字列

以下にあげるものは、Array#packString#unpack のテンプレート文字の一覧です。テンプレート文字は後に「長さ」を表す数字を続けることができます。「長さ」の代わりに`*'とすることで「残り全て」を表すこともできます。

長さの意味はテンプレート文字により異なりますが大抵、

"iiii"

のように連続するテンプレート文字は

"i4"

と書き換えることができます。

テンプレート文字列中の空白類は無視されます。また、`#' から改行あるいはテンプレート文字列の最後まではコメントとみなされ無視されます。

整数のテンプレート文字のシステム依存性

各テンプレート文字の説明の中で、 short や long はシステムによらずそれぞれ 2, 4バイトサイズの数値(32ビットマシンで一般的なshort, longのサイズ)を意味しています。s, S, l, L に対しては直後に _ または ! を "s_" あるいは "s!" のように続けることでシステム依存の short, long のサイズにすることもできます。

i, I (int)のサイズは常にシステム依存であり、n, N, v, V のサイズは常にシステム依存ではない(!をつけられない)ことに注意してください。

つまり、IO#ioctl などで C の構造体を渡すときのように、システム依存のサイズとエンディアンに合わせる必要があるときには s!, S!, i!, I!, l!, L!, q!, Q!, j!, J! を用います。また、ネットワークプロトコルやファイルフォーマットのように、システムに依存しないデータを扱うときには n, N, v, V を用います。

強制的にエンディアンを指定したいときは、リトルエンディアンなら < を、ビッグエンディアンなら > を後ろにつけます。! と組み合わせることも可能です。

まとめると以下のようになります。

エンディアン非依存、整数サイズ非依存 (ネットワークプロトコルなどに適切)
  n: big endian unsigned 16bit
  N: big endian unsigned 32bit
  v: little endian unsigned 16bit
  V: little endian unsigned 32bit
エンディアン依存、整数サイズ依存 (C の構造体などに適切)
  s!: signed short
  S!: unsigned short
  i!: signed int
  I!: unsigned int
  l!: signed long
  L!: unsigned long
  q!: signed long long
  Q!: unsigned long long
  j,j!: intptr_t
  J,J!: uintptr_t
エンディアン依存、整数サイズ非依存 (C99 の stdint.h にある厳密な幅を持つ整数型に適切)
  s: int16_t
  S: uint16_t
  l: int32_t
  L: uint32_t
エンディアンの強制指定(バイナリ解析などに適切)
  S>:  big endian unsigned 16bit(nと同じ)
  s>:  big endian int16_t
  s!>: big endian signed short
  l<:  little endian int32_t
  l!<: little endian signed long

各テンプレート文字の説明

説明中、Array#packString#unpack で違いのあるものは `/' で区切って「Array#pack の説明 / String#unpack の説明」としています。

a

ASCII文字列(ヌル文字を詰める/後続するヌル文字やスペースを残す)


["abc"].pack("a")    # => "a"
["abc"].pack("a*")   # => "abc"
["abc"].pack("a4")   # => "abc\0"

"abc\0".unpack("a4") # => ["abc\0"]
"abc ".unpack("a4")  # => ["abc "]
A

ASCII文字列(スペースを詰める/後続するヌル文字やスペースを削除)


["abc"].pack("A")    # => "a"
["abc"].pack("A*")   # => "abc"
["abc"].pack("A4")   # => "abc "

"abc ".unpack("A4")  # => ["abc"]
"abc\0".unpack("A4") # => ["abc"]
Z

ヌル終端文字列(長さが`*'の場合も含め、ヌル文字を詰める/後続するヌル文字を削除)


["abc"].pack("Z")  # => "a"
["abc"].pack("Z*") # => "abc\0"
["abc"].pack("Z4") # => "abc\0"

"abc\0".unpack("Z4") # => ["abc"]
"abc ".unpack("Z4")  # => ["abc "]
b

ビットストリング(各バイトごとに下位ビットから上位ビット)


"\377\000".unpack("b*") # => ["1111111100000000"]
"\001\002".unpack("b*") # => ["1000000001000000"]
"\001\002".unpack("b3") # => ["100"]


["1000000001000000"].pack("b*") # => "\001\002"
B

ビットストリング(各バイトごとに上位ビットから下位ビット)


"\377\000".unpack("B*")  # => ["1111111100000000"]
"\001\002".unpack("B*")  # => ["0000000100000010"]
"\001\002".unpack("B9")  # => ["000000010"]
"\001\002".unpack("B15") # => ["000000010000001"]

["0000000100000010"].pack("B*")  # => "\001\002"
["0000000100000010"].pack("B0")  # => ""
["0000000100000010"].pack("B1")  # => "\000"
["0000000100000010"].pack("B7")  # => "\000"
["0000000100000010"].pack("B8")  # => "\001"
["0000000100000010"].pack("B9")  # => "\001\000"
["0000000100000010"].pack("B14") # => "\001\000"
["0000000100000010"].pack("B15") # => "\001\002"
["0000000100000010"].pack("B16") # => "\001\002"
h

16進文字列(下位ニブルが先)


"\x01\xfe".unpack("h*") # => ["10ef"]
"\x01\xfe".unpack("h3") # => ["10e"]

["10ef"].pack("h*") # => "\001\376"
H

16進文字列(上位ニブルが先)


"\x01\xfe".unpack("H*") # => ["01fe"]
"\x01\xfe".unpack("H3") # => ["01f"]
"~".unpack("H2")        # => ["7e"]

["01fe"].pack("H*") # => "\001\376"
["7e"].pack("H2")   # => "~"
c

char (8bit 符号つき整数)


"\001\376".unpack("c*") # => [1, -2]

[1, -2].pack("c*")  # => "\001\376"
[1, 254].pack("c*") # => "\001\376"
C

unsigned char (8bit 符号なし整数)


"\001\376".unpack("C*") # => [1, 254]

[1, -2].pack("C*")  # => "\001\376"
[1, 254].pack("C*") # => "\001\376"
s

short (16bit 符号つき整数, エンディアンに依存) (s! は 16bit でなく、short のサイズに依存)

リトルエンディアン:


"\001\002\376\375".unpack("s*") # => [513, -514]

[513, 65022].pack("s*") # => "\001\002\376\375"
[513, -514].pack("s*")  # => "\001\002\376\375"

ビッグエンディアン:


"\001\002\376\375".unpack("s*") # => [258, -259]

[258, 65277].pack("s*") # => "\001\002\376\375"
[258, -259].pack("s*")  # => "\001\002\376\375"
S

unsigned short (16bit 符号なし整数, エンディアンに依存) (S! は 16bit でなく、short のサイズに依存)

リトルエンディアン:


"\001\002\376\375".unpack("S*") # => [513, 65022]

[513, 65022].pack("s*") # => "\001\002\376\375"
[513, -514].pack("s*")  # => "\001\002\376\375"

ビッグエンディアン:


"\001\002\376\375".unpack("S*") # => [258, 65277]

[258, 65277].pack("S*") # => "\001\002\376\375"
[258, -259].pack("S*")  # => "\001\002\376\375"
i

int (符号つき整数, エンディアンと int のサイズに依存)

リトルエンディアン, 32bit int:


"\001\002\003\004\377\376\375\374".unpack("i*") # => [67305985, -50462977]

[67305985, 4244504319].pack("i*") # => RangeError
[67305985, -50462977].pack("i*")  # => "\001\002\003\004\377\376\375\374"

ビッグエンディアン, 32bit int:


"\001\002\003\004\377\376\375\374".unpack("i*") # => [16909060, -66052]

[16909060, 4294901244].pack("i*") # => RangeError
[16909060, -66052].pack("i*")     # => "\001\002\003\004\377\376\375\374"
I

unsigned int (符号なし整数, エンディアンと int のサイズに依存)

リトルエンディアン, 32bit int:


"\001\002\003\004\377\376\375\374".unpack("I*") # => [67305985, 4244504319]

[67305985, 4244504319].pack("I*") # => "\001\002\003\004\377\376\375\374"
[67305985, -50462977].pack("I*")  # => "\001\002\003\004\377\376\375\374"

ビッグエンディアン, 32bit int:


"\001\002\003\004\377\376\375\374".unpack("I*") # => [16909060, 4294901244]

[16909060, 4294901244].pack("I*") # => "\001\002\003\004\377\376\375\374"
[16909060, -66052].pack("I*")     # => "\001\002\003\004\377\376\375\374"
l

long (32bit 符号つき整数, エンディアンに依存) (l! は 32bit でなく、long のサイズに依存)

リトルエンディアン, 32bit long:


"\001\002\003\004\377\376\375\374".unpack("l*") # => [67305985, -50462977]

[67305985, 4244504319].pack("l*") # => RangeError
[67305985, -50462977].pack("l*")  # => "\001\002\003\004\377\376\375\374"
L

unsigned long (32bit 符号なし整数, エンディアンに依存) (L! は 32bit でなく、long のサイズに依存)

リトルエンディアン, 32bit long:


"\001\002\003\004\377\376\375\374".unpack("L*") # => [67305985, 4244504319]

[67305985, 4244504319].pack("L*") # => "\001\002\003\004\377\376\375\374"
[67305985, -50462977].pack("L*")  # => "\001\002\003\004\377\376\375\374"
q

long long (符号付き整数, エンディアンと long long のサイズに依存) (C で long long が扱えない場合には 64bit)

リトルエンディアン, 64bit long long:


"\001\002\003\004\005\006\007\010\377\376\375\374\373\372\371\370".unpack("q*")
# => [578437695752307201, -506097522914230529]

[578437695752307201, -506097522914230529].pack("q*")
# => "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
[578437695752307201, 17940646550795321087].pack("q*")
# => "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
Q

unsigned long long (符号なし整数, エンディアンと long long のサイズに依存) (C で long long が扱えない場合には 64bit)

リトルエンディアン, 64bit long long:


"\001\002\003\004\005\006\007\010\377\376\375\374\373\372\371\370".unpack("Q*")
# => [578437695752307201, 17940646550795321087]

[578437695752307201, 17940646550795321087].pack("Q*")
# => "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
[578437695752307201, -506097522914230529].pack("Q*")
# => "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
m

base64された文字列。60 オクテットごと(と最後)に改行コードが付加されます。

Base64は、3オクテット(8bits * 3 = 24bits)のバイナリコードをASCII文字のうちの65文字 ([A-Za-z0-9+/]の64文字とpaddingのための'=')だけを使用して 4オクテット(6bits * 4 = 24bits)の印字可能文字列に変換するエンコーディング法です。[RFC2045], [RFC4648] で定義されています。


[""].pack("m")             # => ""
["\0"].pack("m")           # => "AA==\n"
["\0\0"].pack("m")         # => "AAA=\n"
["\0\0\0"].pack("m")       # => "AAAA\n"
["\377"].pack("m")         # => "/w==\n"
["\377\377"].pack("m")     # => "//8=\n"
["\377\377\377"].pack("m") # => "////\n"

["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m")
# => "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
["abcdefghijklmnopqrstuvwxyz"].pack("m3")
# => "YWJj\nZGVm\nZ2hp\namts\nbW5v\ncHFy\nc3R1\ndnd4\neXo=\n"

"".unpack("m")       # => [""]
"AA==\n".unpack("m") # => ["\000"]
"AA==".unpack("m")   # => ["\000"]

"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m")
# => ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m")
# => ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]

m0 は [RFC4648] 対応


[""].pack("m0")             # => ""
["\0"].pack("m0")           # => "AA=="
["\0\0"].pack("m0")         # => "AAA="
["\0\0\0"].pack("m0")       # => "AAAA"
["\377"].pack("m0")         # => "/w=="
["\377\377"].pack("m0")     # => "//8="
["\377\377\377"].pack("m0") # => "////"

["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m0")
# => "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg=="

"".unpack("m0")       # => [""]
"AA==\n".unpack("m0") # => ArgumentError
"AA==".unpack("m0")   # => ["\x00"]

"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==".unpack("m0")
# => ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]

@see base64

M

quoted-printable encoding された文字列


["a b c\td \ne"].pack("M") # => "a b c\td =\n\ne=\n"

"a b c\td =\n\ne=\n".unpack("M") # => ["a b c\td \ne"]
n

ネットワークバイトオーダー(ビッグエンディアン)のunsigned short (16bit 符号なし整数)


[0,1,-1,32767,-32768,65535].pack("n*")
# => "\000\000\000\001\377\377\177\377\200\000\377\377"

"\000\000\000\001\377\377\177\377\200\000\377\377".unpack("n*")
# => [0, 1, 65535, 32767, 32768, 65535]
N

ネットワークバイトオーダー(ビッグエンディアン)のunsigned long (32bit 符号なし整数)


[0,1,-1].pack("N*") # => "\000\000\000\000\000\000\000\001\377\377\377\377"

"\000\000\000\000\000\000\000\001\377\377\377\377".unpack("N*") # => [0, 1, 4294967295]
v

"VAX"バイトオーダー(リトルエンディアン)のunsigned short (16bit 符号なし整数)


[0,1,-1,32767,-32768,65535].pack("v*")
# => "\000\000\001\000\377\377\377\177\000\200\377\377"

"\000\000\001\000\377\377\377\177\000\200\377\377".unpack("v*")
# => [0, 1, 65535, 32767, 32768, 65535]
V

"VAX"バイトオーダー(リトルエンディアン)のunsigned long (32bit 符号なし整数)


[0,1,-1].pack("V*") # => "\000\000\000\000\001\000\000\000\377\377\377\377"

"\000\000\000\000\001\000\000\000\377\377\377\377".unpack("V*") # => [0, 1, 4294967295]
f

単精度浮動小数点数(機種依存)

IA-32 (x86) (IEEE754 単精度 リトルエンディアン):


[1.0].pack("f")      # => "\000\000\200?"
[0.0/0.0].pack("f")  # => "\000\000\300\377"      # NaN
[1.0/0.0].pack("f")  # => "\000\000\200\177"      # +Inf
[-1.0/0.0].pack("f") # => "\000\000\200\377"      # -Inf

SPARC (IEEE754 単精度 ビッグエンディアン):


[1.0].pack("f")      # => "?\200\000\000"
[0.0/0.0].pack("f")  # => "\177\377\377\377"      # NaN
[1.0/0.0].pack("f")  # => "\177\200\000\000"      # +Inf
[-1.0/0.0].pack("f") # => "\377\200\000\000"      # -Inf

VAX (NetBSD 3.0) (非IEEE754):


[1.0].pack("f") # => "\200@\000\000"
d

倍精度浮動小数点数(機種依存)

IA-32 (IEEE754 倍精度 リトルエンディアン):


[1.0].pack("d")      # => "\000\000\000\000\000\000\360?"
[0.0/0.0].pack("d")  # => "\000\000\000\000\000\000\370\377"      # NaN
[1.0/0.0].pack("d")  # => "\000\000\000\000\000\000\360\177"      # +Inf
[-1.0/0.0].pack("d") # => "\000\000\000\000\000\000\360\377"      # -Inf

SPARC (IEEE754 倍精度 ビッグエンディアン):


[1.0].pack("d")      # => "?\360\000\000\000\000\000\000"
[0.0/0.0].pack("d")  # => "\177\377\377\377\377\377\377\377"      # NaN
[1.0/0.0].pack("d")  # => "\177\360\000\000\000\000\000\000"      # +Inf
[-1.0/0.0].pack("d") # => "\377\360\000\000\000\000\000\000"      # -Inf

VAX (NetBSD 3.0) (非IEEE754):


[1.0].pack("d") # => "\200@\000\000\000\000\000\000"
e

リトルエンディアンの単精度浮動小数点数(機種依存)

IA-32 (IEEE754):


[1.0].pack("e") # => "\000\000\200?"

SPARC (IEEE754):


[1.0].pack("e") # => "\000\000\200?"
E

リトルエンディアンの倍精度浮動小数点数(機種依存)

IA-32 (IEEE754):


[1.0].pack("E") # => "\000\000\000\000\000\000\360?"

SPARC (IEEE754):


[1.0].pack("E") # => "\000\000\000\000\000\000\360?"
g

ビッグエンディアンの単精度浮動小数点数(機種依存)

IA-32 (IEEE754):


[1.0].pack("g") # => "?\200\000\000"

SPARC (IEEE754):


[1.0].pack("g") # => "?\200\000\000"

IEEE754準拠な環境の場合、以下のようにして符号、指数部、仮数部を取り出せます。


s = [v].pack("g").unpack("B*")[0][0,1]      # 符号
e = [v].pack("g").unpack("B*")[0][1,8]      # 指数部
f = [v].pack("g").unpack("B*")[0][9,23]     # 仮数部

そして、s, e, f の意味は以下の通りです。


sgn = s == "0" ? +1.0 : -1.0
exp = Integer("0b" + e)
fra = Integer("0b" + f)
if exp == 0
  if fra == 0
    sgn * 0                     # ±0 (positive/negative zero)
  else
    sgn * fra * 2**(-126-23)    # 非正規化数 (denormalized number)
  end
elsif exp == 255
  if fra == 0
    sgn * Inf                   # ±∞ (positive/negative infinity)
  else
    NaN                         # 非数 (not a number)
  end
else
  fra += 1 << 23                # ゲタ
  sgn * fra * 2**(exp-127-23)   # 正規化数 (normalized number)
end
G

ビッグエンディアンの倍精度浮動小数点数(機種依存)

IA-32:


[1.0].pack("G") # => "?\360\000\000\000\000\000\000"

SPARC:


[1.0].pack("G") # => "?\360\000\000\000\000\000\000"

IEEE754準拠な環境の場合、以下のようにして符号、指数部、仮数部を取り出せます。


s = [v].pack("G").unpack("B*")[0][0,1]    # 符号
e = [v].pack("G").unpack("B*")[0][1,11]   # 指数部
f = [v].pack("G").unpack("B*")[0][12,52]  # 仮数部

そして、s, e, f の意味は以下の通りです。


sgn = s == "0" ? +1.0 : -1.0
exp = Integer("0b" + e)
fra = Integer("0b" + f)
if exp == 0
  if fra == 0
    sgn * 0                     # ±0 (positive/negative zero)
  else
    sgn * fra * 2**(-1022-52)   # 非正規化数 (denormalized number)
  end
elsif exp == 2047
  if fra == 0
    sgn * Inf                   # ±∞ (positive/negative infinity)
  else
    NaN                         # 非数 (not a number)
  end
else
  fra += 1 << 52                # ゲタ
  sgn * fra * 2**(exp-1023-52)  # 正規化数 (normalized number)
end
p

ヌル終端の文字列へのポインタ


[""].pack("p")             # => "\310\037\034\010"
["a", "b", "c"].pack("p3") # => " =\030\010\340^\030\010\360^\030\010"
[nil].pack("p")            # => "\000\000\000\000"
P

構造体(固定長文字列)へのポインタ


[nil].pack("P")    # => "\000\000\000\000"
["abc"].pack("P3") # => "x*\024\010"

["abc"].pack("P4") # => ArgumentError: too short buffer for P(3 for 4)
[""].pack("P")     # => ArgumentError: too short buffer for P(0 for 1)
u

uuencodeされた文字列


[""].pack("u")           # => ""
["a"].pack("u")          # => "!80``\n"
["abc"].pack("u")        # => "#86)C\n"
["abcd"].pack("u")       # => "$86)C9```\n"
["a"*45].pack("u")       # => "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n"
["a"*46].pack("u")       # => "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n"
["abcdefghi"].pack("u6") # => "&86)C9&5F\n#9VAI\n"
U

UTF-8


[0].pack("U")                               # => "\000"
[1].pack("U")                               # => "\001"
[0x7f].pack("U")                            # => "\177"
[0x80].pack("U")                            # => "\302\200"
[0x7fffffff].pack("U")                      # => "\375\277\277\277\277\277"
[0x80000000].pack("U")                      # => RangeError
[0,256,65536].pack("U3")                    # => "\000\304\200\360\220\200\200"

"\000\304\200\360\220\200\200".unpack("U3") # => [0, 256, 65536]
"\000\304\200\360\220\200\200".unpack("U")  # => [0]
"\000\304\200\360\220\200\200".unpack("U*") # => [0, 256, 65536]
w

BER圧縮整数

1バイトあたり7ビットを使用して必要最小限のバイト数で任意サイズの 0以上の整数を表す数値表現。各バイトの最上位ビットはデータの最後を除いて必ず1が立っている(つまり最上位ビットはどこまでデータがあるかを示している)。

ISO/IEC 8825-1:1995 : Information technology−ASN.1 encoding rules : Specification of Basic Encoding Rules(BER) に定められる整数の符号化方法。


[0].pack("w")             # => "\000"
[1].pack("w")             # => "\001"
[127].pack("w")           # => "\177"
[128].pack("w")           # => "\201\000"
[0x3fff].pack("w")        # => "\377\177"
[0x4000].pack("w")        # => "\201\200\000"
[0x3fffffff].pack("w")    # => "\203\377\377\377\177"
[0x40000000].pack("w")    # => "\204\200\200\200\000"
[0xffffffff].pack("w")    # => "\217\377\377\377\177"
[0x100000000].pack("w")   # => "\220\200\200\200\000"

"\0".unpack("w")          # => [0]
"\0\201\0\1".unpack("w*") # => [0, 128, 1]

なお、BER圧縮整数でエンコードした結果は大小関係を保存しない。たとえば、[0x3fff].pack("w") > [0x4000].pack("w") である。

x

ヌルバイト(pack)/1バイト読み飛ばし(unpack)


[97, 98].pack("CxC")    # => "a\x00b"
[97, 98].pack("Cx3C")   # => "a\x00\x00\x00b"

"abc".unpack("CxC")  # => [97, 99]
"abc".unpack("Cx3C") # => ArgumentError: x outside of string
X

1バイト後退


[97, 98, 99].pack("CCXC") # => "ac"

"abcdef".unpack("x*XC") # => [102]
@

絶対位置への移動


[97, 98].pack("C @3 C") # => "a\x00\x00b"

"abcd".unpack("C @3 C") # => [97, 100]
j

intptr_t (ポインタの幅の符号つき整数, エンディアンに依存)

J

uintptr_t (ポインタの幅の符号なし整数, エンディアンに依存)

使用例

以下、pack/unpack の使用例の一部です。

pack を使用しなくても同じことができる場合はその例も載せています。 pack は暗号になりやすい面があることを考慮し、pack を使いたくない人に別解を示すためです。

数値(文字コード)の配列を文字列に変換する例

[82, 117, 98, 121].pack("cccc")  # => "Ruby"
[82, 117, 98, 121].pack("c4")    # => "Ruby"
[82, 117, 98, 121].pack("c*")    # => "Ruby"

s = ""
[82, 117, 98, 121].each {|c| s << c}
s    # => "Ruby"

[82, 117, 98, 121].collect {|c| sprintf "%c", c}.join   # => "Ruby"

[82, 117, 98, 121].inject("") {|s, c| s << c}    # => "Ruby"
文字列を数値(文字コード)の配列に変換する例

"Ruby".unpack('C*')    # => [82, 117, 98, 121]

a = []
"Ruby".each_byte {|c| a << c}
a    # => [82, 117, 98, 121]
"x" でヌルバイトを埋めることができる

[82, 117, 98, 121].pack("ccxxcc")    # => "Ru\000\000by"
"x" で文字を読み飛ばす事が出来る

"Ru\0\0by".unpack('ccxxcc')    # => [82, 117, 98, 121]
Hexダンプを数値の配列に変換する例

"61 62 63 64 65 66".delete(' ').to_a.pack('H*').unpack('C*')
# => [97, 98, 99, 100, 101, 102]

"61 62 63 64 65 66".split.collect {|c| c.hex}
# => [97, 98, 99, 100, 101, 102]
バイナリと16進数のpackでは長さ指定は生成されるバイト数ではなく、ビットやニブルの個数を表す

[0b01010010, 0b01110101, 0b01100010, 0b01111001].pack("C4")
# => "Ruby"
["01010010011101010110001001111001"].pack("B32") # 8 bits * 4
# => "Ruby"

[0x52, 0x75, 0x62, 0x79].pack("C4")
# => "Ruby"
["52756279"].pack("H8")  # 2 nybbles * 4
# => "Ruby"
テンプレート文字'a'の長さ指定は1つの文字列だけに適用される

["RUBY", "u", "b", "y"].pack("a4")
# => "RUBY"

["RUBY", "u", "b", "y"].pack("aaaa")
# => "Ruby"

["RUBY", "u", "b", "y"].pack("a*aaa")
# => "RUBYuby"
テンプレート文字"a"は、長さが足りない分をヌル文字で補う

["Ruby"].pack("a8")
# => "Ruby\000\000\000\000"
リトルエンディアンとビッグエンディアン

[1,2].pack("s2")
# => "\000\001\000\002" # ビッグエンディアンのシステムでの出力
# => "\001\000\002\000" # リトルエンディアンのシステムでの出力

[1,2].pack("n2")
# => "\000\001\000\002" # システムによらずビッグエンディアン

[1,2].pack("v2")
# => "\001\000\002\000" # システムによらずリトルエンディアン
ネットワークバイトオーダの signed long

s = "\xff\xff\xff\xfe"
n = s.unpack("N")[0]
if n[31] == 1
  n = -((n ^ 0xffff_ffff) + 1)
end
n # => -2
ネットワークバイトオーダの signed long(その2)

s = "\xff\xff\xff\xfe"
n = s.unpack("N").pack("l").unpack("l")[0]
n # => -2
IPアドレス

require 'socket'
Socket.gethostbyname("localhost")[3].unpack("C4").join(".")
# => "127.0.0.1"

"127.0.0.1".split(".").collect {|c| c.to_i}.pack("C4")
# => "\177\000\000\001"
sockaddr_in 構造体

require 'socket'
[Socket::AF_INET,
 Socket.getservbyname('echo'),
 127, 0, 0, 1].pack("s n C4 x8")
# => "\002\000\000\a\177\000\000\001\000\000\000\000\000\000\000\000"

pack/unpack を使う代わりに Socket.pack_sockaddr_in, Socket.unpack_sockaddr_in メソッドがあります。

'\0'終端文字列のアドレス

テンプレート文字 "p" や "P" は、C 言語レベルのインタフェースのためにあります(例えば IO#ioctl)。


["foo"].pack("p")    # => "8\266\021\010"

結果の文字列はゴミに見えますが、実際は文字列"foo\0"を指すアドレス(のバイナリ表現)です。以下のようにすれば見慣れた表記で見ることが出来ます


printf "%#010x\n", "8\266\021\010".unpack("L")[0] # => 0x0811b638

アドレスが指す先のオブジェクト(この例で "foo\0") は、pack の結果が GC されるまではGCされないことが保証されています。

unpack("p"), unpack("P") は、pack の結果からしか unpack できません。


["foo"].pack("p").unpack("p") # => ["foo"]
"8\266\021\010".unpack("p")
# => -:1:in `unpack': no associated pointer (ArgumentError)
#         from -:1

"p" や "P" は、nil を特別に扱い NULL ポインタとして解釈します。(以下は、32bitマシンで一般的な結果)


[nil].pack("p")        # => "\000\000\000\000"
"\0\0\0\0".unpack("p") # => [nil]
構造体のアドレス

例えば、

      struct {
        int   a;
        short b;
        long  c;
      } v = {1,2,3};

を表す文字列は


v = [1,2,3].pack("i!s!l!")

です。(byte alignment の問題から実際は適当な padding が必要になるかもしれません)

この構造体を指すアドレスは


[v].pack("P")  # => "\300\265\021\010"

で得られます。

UTF-8からUCS-2への変換 (サロゲートを処理していないので UTF-16 とはいえない)

Little endian:


("Comments").unpack("U*").pack("v*") # => "C\000o\000m\000m\000e\000n\000t\000s\000"

Big endian:


("Comments").unpack("U*").pack("n*") # => "\000C\000o\000m\000m\000e\000n\000t\000s"