バイナリデータを扱う
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”です。
コメント