|
Tcl 8.0以降では、バイナリデータを扱うTclコマンドbinary
が使えます。感じとしてはPerlの pack/unpack関数に酷似していて、
バイナリデータをファイルに書いたり、バイナリファイルからデータを数値として読んだりすることができます。
バイナリデータから数値または文字の配列に変換するのが
binary scan、
数値または文字の配列からバイナリデータに変換するのが
binary formatです。
set b "0x08 0x05 0x0c 0x0c 0x0f 0x2c 0x17 0x0f 0x12 0x0c 0x04 0x2e 0x0a"
まずこんなサンプルのバイナリデータ(*1)を用意すると。
(*1)Muleで見ると「^H^E^L^L^O,^W^O^R^L^D.\n」となるのを狙っていたのですが、
^Oは表示されないようです。残念。
これらのコマンドには、引数として「フォーマット指示子」
とゆーものを与えます。
これはバイナリデータと数値なり文字なりの配列をどのように変換するかを指定するもので、
「英文字記号1個(c,h,Hなど)+数字または*」が1つ以上並んだもの
という形をしています。
数字は、その前の英文字記号によって多少解釈は違いますが、主に変換するデータのバイト数を指定します。
「*」を指定すると変換対象の文字列に入っている全バイトが変換されます。
また数値が1の場合は省略できます。
'c'記号は、バイナリデータをそのASCIIコードの(10進数の)数値のリストに変換します(*2)。
% binary scan $b c5 result
8 5 12 12 15
% binary scan $b c result
8
% binary scan $b c* result
8 5 12 12 15 44 23 15 18 12 4 46 10
(*2)ここでは便宜上変換結果がbinary scanコマンドの戻り値であるかのように書いていますが、
実際には変数resultに入るのがこの値という意味です。
逆に
set array {8 5 12 12 15 44 23 15 18 12 4 46 10}
set b [binary format c* $array]
とすればもとのバイナリデータに戻ります。
'H'記号は、バイナリデータをビッグエンディアンの16進数の文字の羅列に変換します。
% binary scan $b H10 result
08050c0c0f
% binary scan $b H3 result
080
% binary scan $b H* result
08050c0c0f2c170f120c042e0a
「H10」というと、変換後の文字列の長さが10バイト、という意味です。
つまりもとのバイナリデータのバイト数としてはその半分の5バイト分しか使われないのに注意が必要です。
逆に言うと、H* で変換するさい、
結果の文字列の長さはもとのバイナリデータのバイト数のいつでもちょうど倍になります。
「ビッグエンディアンの16進数」というとなにやらふがふがですが、
もとのバイナリデータ $b の "0x08 0x05 0x0c 0x0c 0x0f ..."
と上の結果の "08050c0c0f ..."とゆーのを見比べるとなんとなくわかると思います。
'h'記号は、バイナリデータをリトルエンディアンの16進数の文字の羅列に変換します。
% binary scan $b h10 result
8050c0c0f0
% binary scan $b h3 result
805
% binary scan $b h* result
8050c0c0f0c271f021c040e2a0
'h'での結果と'H'での結果は、互いの
奇数バイト目とその後ろの偶数バイト目を1対ずつひっくり返したものになります。
(エンディアンとゆーのはそういうものだということで、
詳しくはここでは省略します。)
'S'記号は、先頭から2バイト(16ビット)ずつをビッグエンディアンのshort
の数値に変換して、その10進数表記のリストを返します。
's'はそのリトルエンディアン版です。
% binary scan $b S3 result
2053 3084 3884
% binary scan $b S result
2053
% binary scan $b S* result
2053 3084 3884 5903 4620 1070
'I'記号は、先頭から4バイト(32ビット)ずつをビッグエンディアンのint
の数値に変換して、その10進数表記のリストを返します。
'i'はそのリトルエンディアン版です。
% binary scan $b I* result
134548492 254547727 302777390
'S'や'I'の場合は、それぞれデータを2バイト、4バイトずつ区切って変換しますが、
はんぱが残った場合には空いたところのビットとして零が自動的に補われます。
最後に、これらのフォーマット指示子を何個か並べて指定することもできます。
binary scan $b S3c5 result1 result2
とすると、result1に「2053 3084 3884」、
result2に「23 15 18 12 4」がセットされます。
フォーマット指示子を2つ指定すると結果を格納する変数も2つ
(ここでのresult1、result2)指定しないとエラーになるので注意が必要です
(必要以上に変数の数が多いのは無視されるだけで無害です)。
この例では、
"0x08 0x05 0x0c 0x0c 0x0f 0x2c 0x17 0x0f 0x12 0x0c 0x04 0x2e 0x0a"
の先頭から6バイトが 'S3'に従って3つのshort数に、
7,8,9,10,11バイト目がそれぞれ 'c5'に従ってASCIIコードの数値に変換されます。
「binary format」コマンドでこの逆ができるのはもちろんです。
数値データをコンパクトにバイナリファイルに保存するのに、binary formatコマンドが使えます。
次の例は、数値データの配列の各要素をASCIIコードとして文字に置き換えて保存している例です。
フォーマットの "c" が、ASCIIコードとして変換することを表します。
set fout [open outfile.dat w]
fconfigure $fout -translation binary
set cdata {1 3 5 2 4 6 11 13 15 20}
set bdata [binary format c* $cdata]
puts -nonewline $fout $bdata
close $fout
|
binary scanコマンドを使って、バイナリファイルの親玉格である画像ファイルの一種、
ウィンドウズビットマップ(BMP)画像ファイルの先頭部分を覗いてみます。
BMPの先頭部分(ヘッダ)にはいろいろな画像情報が格納されていますが、
その詳しいところは今回は省略します。
proc getBMPID {fp} {
set a [read $fp 2]
if {"$a" != "BM"} { error "cannot get BMP ID string." }
return $a
}
proc getLong {fp} {
set a [read $fp 4]
binary scan [read $fp 4] i value
return $value
}
proc getShort {fp} {
binary scan [read $fp 2] s value
return $value
}
proc getBMPInfo {filename} {
set fp [open $filename r]
fconfigure $fp -translation binary
getBMPID $fp
set headersize1 [getLong $fp]
set reserved [getLong $fp]
set offset [getLong $fp]
set headersize2 [getLong $fp]
set imagewidth [getLong $fp]
set imageheight [getLong $fp]
set planes [getShort $fp]
set bitspardot [getShort $fp]
close $fp
return [list $headersize1 $reserved $offset $headersize2\
$imagewidth $imageheight $planes $bitspardot]
}
getBMPInfo "nttcodomo.bmp"
実際に何かBMPファイルを読ませてみると、
ヘッダサイズ1:14、予約領域:0、オフセット:54、
ヘッダサイズ2:40となっているのが普通です。
で、$imagewidthと$imageheightにその画像の幅と高さがそれぞれ入っているはずです。
$bitspardotは1画素(ピクセル)を表現するのに必要としているビット数で、
16色画像なら4、256色画像なら8、フルカラー画像なら24です。
おっと、このようにバイナリデータをファイルから読んだり、
ファイルに書きこんだりするときには、
fconfigureコマンドの-translation binaryスイッチ
を使ってファイルチャネルをバイナリ対応にしておく必要があります。
でないとひどい目に遭うことがあるので、ご用心。
(first uploaded 1999/06/18 last updated 2004/01/13, MISUMI URANO)
|