Tcl – バイナリデータの操作(1)

バイナリデータを扱う

Tclは基本的に文字列データを操作するようになっています。Tcl8.0からバリナリデータを操作する為にbinaryコマンドが追加されました。

[書式]

binary format formatString ?arg arg ...?
binary scan string formatString ?varName varName ...?

binaryコマンドの第1引数にformatを指定すると、文字列をバイナリに変換します。第1引数にscanを指定した場合はバイナリデータを文字列に変換します。

binaryコマンドを使うと文字列とバリナリデータを相互に変換することができます。

binaryコマンドのオプションは多数あります。最初にオプションを1つ1つ説明するよりも、まずは、バイナリデータの操作例を見て頂いてから、オプションの説明をした方が理解しやすいと思うので、バイナリデータの操作方法を紹介します。

バイナリデータの操作方法の説明でわからない部分は、オプションの説明を見た後に、再度バイナリデータの操作方法を見て頂くと、意味がわかるのではないかと思います。

※ファイルは前回使用のstr.txtを使います。

Hello! Hello!
Good morning!
Bye bye!
Have a nice day!
こんにちわ
おはようございます。
バイバイ
よい1日を!

[注意]
str.txtはUTF-8でエンコードされた文字列が書き込まれたファイルです。この記事の説明は、Tclの内部コードがUnicode(U+xxxx相当)で符号化方式をUTF-8で使用している環境で説明しています。

バイナリデータのファイルを読み書きする

バイナリデータを読み書きする方法を紹介します。

バイナリデータのファイルを読み込む

以下の例はバイナリデータのファイルを読み込んだ例です。

[使用例]

$ tclsh

% set fid [open str.txt r]
file5
% fconfigure $fid -translation binary
% puts [read $fid]                  
Hello! Hello!
Good morning!
Bye bye!
Have a nice day!
こんにちã‚
おはã‚ãã”ãã
                 ます。
ãƒ

str.txtを文字データではなくバリナリデータとして読み込むには読み込むチャネルの設定を以下のように、binary に変更します。

fconfigure $fid -translation binary

または

chan configure $fid -translation binary

-translation binary を指定すると、改行コードの変換や、ファイルの終端(null)、文字コードのエンコードが無効にされます。

よって、readコマンドやgetsコマンドで読み込んだデータはただのバイトデータとして扱われます。

バイトデータが英数字(asciiコード)の範囲内であれば、文字として画面へ出力されますが、範囲外のバイトデータはエンコードが無効の為、文字化けします。

以下は、str.txtをUNIXのhexdumpコマンドで出力したものです。

$ hexdump str.txt -C
00000000  48 65 6c 6c 6f 21 20 48  65 6c 6c 6f 21 0a 47 6f  |Hello! Hello!.Go|
00000010  6f 64 20 6d 6f 72 6e 69  6e 67 21 0a 42 79 65 20  |od morning!.Bye |
00000020  62 79 65 21 0a 48 61 76  65 20 61 20 6e 69 63 65  |bye!.Have a nice|
00000030  20 64 61 79 21 0a e3 81  93 e3 82 93 e3 81 ab e3  | day!...........|
00000040  81 a1 e3 82 8f 0a e3 81  8a e3 81 af e3 82 88 e3  |................|
00000050  81 86 e3 81 94 e3 81 96  e3 81 84 e3 81 be e3 81  |................|
00000060  99 e3 80 82 0a e3 83 90  e3 82 a4 e3 83 90 e3 82  |................|
00000070  a4 0a e3 82 88 e3 81 84  31 e6 97 a5 e3 82 92 ef  |........1.......|
00000080  bc 81 0a                                          |...|
00000083

0a:改行コード(\n)

str.txtの文字列「こんにちわ」は、10進で55バイト目(16進で36バイト目)の位置にあります。

UTF-8では1文字を、ASCIIコードの範囲内は1バイト、それ以外の文字は2~6バイドで符号化します。0a(改行)までが1行の文字列なので、「こんににわ」の5文字は、1文字3バイトで構成されており、合計15バイトあることがわかります。

e38193 e38293 e381ab e381a1 e3828f 0a
 こ   ん   に   ち   わ  \n

特定箇所のバイナリデータを読み込む

binary scanを使ってバイナリデータを文字列に変換する。

以下の例は、str.txtの「こんにちわ」の部分をバイナリデータとして読み込んで、読み込んだバイナリデータを16進数の表記と文字に変換させています。

[使用例]

$ tclsh

% set fid [open str.txt r]
file5
% fconfigure $fid -translation binary

% seek $fid 54 start              ----(1) 現在位置を移動
% set str [read $fid 16]          ----(2) 16バイド分を読み込み
こんにちã‚

% binary scan $str H* tmp         ----(3.1) 16進数に変換
1
% puts $tmp                       ----(3.2) 16進数で表示
e38193e38293e381abe381a1e3828f0a

% puts [encoding convertfrom utf-8 $str] ----(4) 文字コードに変換
こんにちわ

% close $fid
% exit

(1) 現在位置を移動
hexdumpの出力結果から、「こんにちわ」は10進で55バイト目にあることがわかっています。よって、直前の54バイト目まで現在位置をseekコマンドで移動させます。

(2) 16バイド分を読み込み
通常readコマンドの引数には文字数で指定します。binaryの指定によりエンコード・改行変換が無効になり、ただのバイトデータとして扱われるので、1文字1バイト(1バイト文字)として指定する。「こんにちわ」+ 改行(0a)の16バイト読み込む。読み込んだデータは文字の扱いではないので画面へ出力すると文字化けします。

(3) 16進数に変換・表示
読み込んだバイナリデータをbinary scanコマンドを使って16進数表記に変えて出力しています。引数Hは、バイナリデータを16進数表記の文字列に変換します。

(4) utf-8 に変換
変数strはバイナリデータなので、そのまま画面に出力すると文字化けします。

encoding convertfrom ?encoding? data 

このコマンドは、data を指定した encoding からUnicode(U+xxxx相当)に変換します。例の場合、変数strがUTF-8でエンコードされているとみなすので、結果的に文字として出力されます。

例えば、

encoding convertfrom utf-8 \xe3\x81\x93
encoding convertfrom shiftjis \x82\xb1
encoding convertfrom euc-jp \xa4\xb3
=> こ

上の3つの引数detaの部分は、「こ」をそれぞれのエンコーディングで表した文字コードです。出力時は、システムの環境に合わせて出力されるのでUnix、Windows、Mac OS Xどの環境であっても、ひらがなの「こ」が表示されます。

Unicode(U+xxxx相当)のデータを、引数encodingで指定したエンコードのデータに変換するには、以下のようにします。

encoding convertto shiftjis \u3053
=> 0x82b1 相当のバイナリデータ

ただし、変換された結果はTcl上では文字ではなくバイナリデータとしての扱いです。Unicodeの\u3053とSJISの0x82b1は平仮名の「こ」の文字コードになります。

binary scan [encoding convertto shiftjis \u3053] H* str
=> 1
puts $str
=> 82b1

binary scanの引数formatStringに”H*”を指定すると、バイナリデータを16進コードの文字列に変換します。

バイナリデータの書き込み

binary formatを使って文字列をバイナリデータに変換する。

以下の例は、文字列と16進数の表記の文字列をバイナリデータに変換してファイルに書き込みしています。

[使用例]

$ tclsh

% set str abcdefg           ----(1) 文字列
abcdefg
% set hex 00fffeaa55aa55ff  ----(2) 16進数
00fffeaa55aa55ff

% set fid [open hex.bin w]
file5
% fconfigure $fid -translation binary

% puts -nonewline $fid [binary format a* $str]  ----(3)
% puts -nonewline $fid [binary format H* $hex]  ----(4)

% close $fid
% exit

$ cat hex.bin          ----(5)
abcdefg���U�U�

$ hexdump hex.bin -C   ----(6)
00000000  61 62 63 64 65 66 67 00  ff fe aa 55 aa 55 ff     |abcdefg....U.U.|
0000000f

(1)(2)
文字列と16進数表記のデータを準備。

(3)(4)
引数aは、文字列データをバイナリデータに変換します。引数Hは、16進数表記の文字列をバイナリデータに変換します。

(5)
バイナリデータを書き込んだファイルなので、画面に出力すると文字化けします。

(6)
UNIXのhexdumpコマンドで確認すると、文字ではなくバイトデータとして書き込まれていることが確認できます。
*ASCIIコードの55は大文字の”U”です。


コメント

タイトルとURLをコピーしました