pack テンプレート文字列
以下にあげるものは、Array#pack、String#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! を用います。 また、ネットワークプロトコルやファイルフォーマットのように、 システムに依存しないデータを扱うときには 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
- エンディアン依存、整数サイズ非依存 (C99 の stdint.h にある厳密な幅を持つ整数型に適切)
-
s: int16_t S: uint16_t l: int32_t L: uint32_t
各テンプレート文字の説明
説明中、Array#pack と String#unpack で違いのあるものは `/' で区切って 「Array#pack の説明 / String#unpack の説明」としています。
- a
-
ASCII文字列(null文字を詰める/後続するnull文字やスペースを残す)
["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文字列(スペースを詰める/後続するnull文字やスペースを削除)
["abc"].pack("A") # => "a" ["abc"].pack("A*") # => "abc" ["abc"].pack("A4") # => "abc " "abc ".unpack("A4") # => ["abc"] "abc\0".unpack("A4") # => ["abc"]
- Z
-
null終端文字列(aと同じ / 後続するnull文字を削除)
["abc"].pack("Z") # => "a" ["abc"].pack("Z*") # => "abc" ["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
-
ナルバイト/1バイト読み飛ばす
[?a, ?b].pack("CxC") # => "a\000b" [?a, ?b].pack("Cx3C") # => "a\000\000\000b" "a\000b".unpack("CxC") # => [97, 98] "a\377b".unpack("CxC") # => [97, 98] "a\377b".unpack("Cx3C") # => ArgumentError: x outside of string
- X
-
1バイト後退
[?a, ?b, ?c].pack("CCXC") # => "ac" "abcdef".unpack("x*XC") # => [102]
- @
-
絶対位置への移動
[?a, ?b].pack("C @3 C") # => "a\000\000b" "a\000\000b".unpack("C @3 C") # => [97, 98]
使用例
以下、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"