Subject: UTF-16テキストの注意 Date: 2011年1月30日 15:38:31JST [一部改変] 先日,Perl で UTF-16 のテキストを EUC-JP に変換すると,行数が大幅に減り, 一部データが消えてしまうが,何が原因か,という質問が来ました。 調査した結果,データが消えているのではなく,改行文字の解釈の違いにより, 行数が違うように表示されてしまうことが原因であることが判明しました。 \x0a というバイトは,ASCII, EUC-JP, UTF-8 などでは,単独で制御文字の 改行文字として解釈されますが,UTF-16 では改行以外の文字の一部としても 現れます。wc が UTF-16 に対応していないため (少なくとも,デフォルトでは), この文字の一部の \x0a も改行と認識して,行数を計算します。 しかし,この,改行文字以外の文字の一部として出現する \x0a は,UTF-8 に 変換すると別のバイトにマップされるため,変換後に wc で行数を数える時には 存在しません。 というわけで,UTF-8 や EUC-JP に変換すると,wc では,行数が減ったように 見えてしまう,というわけです。 ■突っ込んだ話 その1:\x0a, \x0d を含む図形文字 UTF-16 を解釈できるツールAでテキストを表示し,それを他のツールBの ウィンドウに貼付けたら,一部が文字化けしていました。バイト値を見て 確認した結果,ツールBは,内部で改行コードをすべて \x0d として処理し, \x0a を \x0d に自動で変換するようになっているため,テキストを 貼付けた時にも,図形文字の一部である \x0a が \x0d に置き換わってしまい, 別の文字になってしまった,というのが原因であることがわかりました。 コードポイントが近いので,割と似たような漢字に置き換わってしまい, ちょっと見では気付かなかったりするので,やっかいかも。 ■突っ込んだ話 その2:エンディアン UTF-16のテキストで,改行文字も2バイトになっていることを確認しようとして, 次のスクリプトを実行してみたが,予想に反して,空行しか出力されない。 1. perl -ne 'print if ( /\x00\x0a/ )' input.txt 次のように,バイトの順序を変えても,変な結果になる。 2. perl -ne 'print if ( /\x0a\x00/ )' input.txt 原因は,byte order と改行コードの解釈でした。  BE \x00 \x0a  LE \x0a \x00 上のスクリプトでは,文字コードを指定していないので,バイト列として 処理します。(これは意図通り。) しかし,そうすると,U+000A に当たる 2バイトの組を改行文字として扱うのではなく,\x0a という1バイトを 改行コードとして扱う。ファイルは LE だったので,本来1文字を 構成するはずの2バイト目が,次の行の先頭として扱われてしまい, 結果として,/\x00\x0a/ にマッチするのが空行だけになる,ということが わかりました。 ■突っ込んだ話 その3:Byte Order Mark Perl で文字コードを変換しようとすると,エラーがたくさん出るというので, エラーメッセージを送ってもらったところ,変換できない文字というのが BOM であることが判明しました。複数のファイルを1つに連結してから 文字コードを変換しようとしたため,ファイルの先頭以外に現れた BOM が 解釈できず,エラーとなったことがわかりました。 参考まで。 -- 名古屋大学大学院国際開発研究科 国際コミュニケーション専攻 大名 力 (Tsutomu OHNA)