[PHP-users 12319] Re: unpack

Moriyoshi Koizumi php-users@php.gr.jp
Tue, 24 Dec 2002 12:36:29 +0900


小泉です。

unpack() の説明は前からちょっとひどいと思っていました。
(翻訳が悪いわけじゃないですよ…)

なのでFAQかなと思い、簡単に解説してみることにします。

長いメールですみません。興味のない方は読み飛ばしてください。
------------------------------------------------------------------------
unpack のフォーマットは

[サイズ指定][反復数][プレフィックス] "/"
[サイズ指定][反復数][プレフィックス] "/"

の繰り返しから成っています。

[サイズ指定]に入る文字は pack() の説明に出てくるものと同一です。
[反復数]に関しても pack() の説明と同一です。
[プレフィックス]は、省略することもできますが、
結果の配列の番号の前に付けられる任意の文字列ということになります。

<?php
var_dump(unpack("c5hoge/A5funi/A*hoehoe", "ABCDEFGHIJKLMN        "));
?>
の結果は

array(7) {
  ["hoge1"]=>
  int(65)
  ["hoge2"]=>
  int(66)
  ["hoge3"]=>
  int(67)
  ["hoge4"]=>
  int(68)
  ["hoge5"]=>
  int(69)
  ["funi"]=>
  string(5) "FGHIJ"
  ["hoehoe"]=>
  string(4) "KLMN"
}

のようになります。また、

<?php
var_dump(unpack("N2/A*", "\x00\x00\x00\x01\x00\x00\x00\x02abc"));
?>

は、

array(3) {
  ["1"]=>
  int(1)
  ["2"]=>
  int(2)
  [""] =>
  string(3) "abc"
}

のようになります。

プレフィックスを省略すると、番号だけになりますが、このとき数字は 0 からで
はなく 1 からスタートすることに注意してください。これで頭を抱える方も
多そうですので。

また、"A" "a" "H" "h" に関しては、反復数はプレフィックスの後に付かないこと
に注意してください。これは pack() の説明にもありますが。

なお、hoge1 〜 hoge5 に入っている 65 とかの数値は、"A" や "B" のいわゆる
文字コードです。詳しくは ord() (http://jp.php.net/ord) を参照してください。

よく掲示板などで csv状のフォーマットを使って一件ごとのデータを格納する例が
ありますが、csv は固定長のレコードではないので、
(注: 1 レコード = 1 記事とします) 特定のレコードだけを取り出すときなどに
は都合が悪いときがあります。そんなときは固定長のレコードにすると便利なの
ですが、それは pack() / unpack() で簡易に実現することができます。

例として、

$octets = pack("NNa32a128a17", $kiji_no, $thread_no, $toukousha, $mail_addr, $date);

fwrite($fp, $octets);

のようにして書き出した文字列は、

$octets = fread($fp, 4 + 4 + 32 + 128 + 17);
$kakikomi = unpack("N記事番号/Nスレッド番号/a32投稿者/a128メールアドレス
/a17投稿日時", $octets);

で読み出すことができます。

4 + 4 + 32 + 128 + 17 と、すこし冗長に見える部分は、単純に pack() で作ら
れた文字列の長さを計算しているだけです。記事番号のサイズ指定文字は "N" で、 
unsigned long 型だから 4 バイト、[投稿者]は最大 32 文字… という具合です。

ここで気をつけなければならないのは、$octets は普通のテキストではないので、
fopen() でファイルを開くときには、バイナリモードにしなければならないとい
うことでしょう。Windows 版でもデフォルトはバイナリモードになっていますが、
何故か Windows 版 Apache DSO を使うときだけデフォルトがテキストモードに
なっています。念のため "wb" "rb" を使うようにしてください。
------------------------------------------------------------------------