■2008-03-29
* [Perl][メモ] JSON::XSのエンコーディング/コードセット関連のフラグ
2010-04-06追記:
モジュール使ってみたけど文字化けして困る〜という方はこのページよりもこちらを先に読まれることをお勧めします。
以前自分でもJSONのフラグについて簡単に説明してきましたが、JSON::XSのドキュメントにずばりENCODING/CODESET FLAG NOTESというのが加わりましたので、その要約と、せっかくだからその部分の全訳を載せます(作者了解済)。(※2008-06-11 下段に一文を追記)
- 1 utf8オフ(デフォルト):
encodeはUnicodeなJSONを返すdecodeはUnicodeなJSONを受けとり、UnicodeなPerlデータを返す。 - 2 utf8オン:
encodeはUTF-8なJSONを返すdecodeはUTF-8なJSONを受け取り、UnicodeなPerlデータを返す。 - 3 asciiオン:
encodeはasciiなJSONを返す(128以上のキャラクタは\uでエスケープ)。 - 4 latin1オン:
encodeはlatin1なJSONを返す(256以上のキャラクタは\uでエスケープ)。 - ※3、4いずれにしろdecodeされた結果はUnicodeなPerlデータ。
なお、encodeに渡すPerlデータがUnicodeであるかないかを気にする必要はないです。モジュールが良いように計らってくれます。(※2008-06-11追記:単純に入力という観点からは気にしなくて良いのですが、実用上、非Unicodeをencodeするならutf8オフに、Unicodeならutf8オンにしてください。そうしないと出力結果はあなたの期待を裏切るかもしれません)この点については、同じドキュメント内にある"A FEW NOTES ON UNICODE AND PERL"が、ある種PerlのUnicodeチュートリアルになっていますので、そちらもお勧めです。
■エンコーディング/コードセット関連のフラグについて
興味ある読者は、エンコーディングあるいはコードセットを意味するフラグ -utf8
,latin1
そしてascii
を目にしてきたことだろう。これらが何をするものなのかについては、いくつかの混乱が見受けられる。そこで手短にこれらの比較をしておく:
utf8
はencode
が出力する(そしてdecode
が期待する)JSONテキストがUTF-8でエンコードされているかどうかをコントロールする。これに対してlatin1
とascii
はただencode
がそれぞれのコードセット範囲を超えた値を持つキャラクタをエスケープするかどうかをコントロールする。これらのフラグは互いに衝突しない。組み合わせによっては他の場合よりも無意味になることはあっても。
すべてのフラグはencode
とdecode
の双方で対称的になるよう注意が払われている。つまりこれらのフラグの組み合わせでエンコードされたJSONテキストは、一般的に、同じフラグが使われている時に正しくデコードされるであろうということだ。エンコード時のフラグと違う状態でデコードするなら、どこかにバグが入り込むこともありうる。
以下はこれらのフラグについての冗長な説明である。「コードセット」とは単にキャラクターとコードポイントの組についての抽象的集合であることに注意せよ。一方、あるエンコーディングはそれらのコードポイント番号を使って(この場合)オクテットにエンコードする。Unicodeは(数多あるうちの)一つのコードセットであり、UTF-8はエンコーディングである。そしてISO-8859-1(= latin1)とASCIIはどちらもコードセットであると「同時に」エンコーディングである。これが混乱の元なのだろう。
□utf8
オフ
utf8
フラグがオフのとき(これがデフォルト)、encode
/decode
はUnicodeの文字列を出力、ないしは期待している。つまりUnicode値(255より大)を持つキャラクタはそのままencodeされる。そして同様にUnicodeキャラクタはそのままdecodeされる。そこに変化はない。ただ、それぞれ【encodeは】Unicodeコードポイントとして、あるいは【decodeは】Unicodeキャラクタとして(再)解釈しているだけだ。(あなたが奇妙/奇天烈/お馬鹿な処理をしない限り、Perlにとってそれらは文字列内において同じものである)。
これはあなた自身でエンコードを扱いたい時(例えばUTF-16でエンコードされたJSONテキストが欲しい時など)、あるいは他のレイヤーがあなたのためにエンコード処理をしてくれるとき(例えば、透過的にUTF-8にエンコードするファイルハンドルを使って端末にprintするとき、あなたは明らかに自分のデータをまずUTF-8にエンコードするなどということをしたくはなく、別のタイミングでPerlにやらせたいであろう)に便利である。
□utf8
オン
もしutf8
フラグがオンであるなら、encode
/decode
は全てのキャラクタを対応するUTF-8マルチバイトでもってエンコードするだろう。あるいは入力された文字列がUTF-8でエンコードされていることを期待するだろう。これは、【decodeに】入力された文字列が255より大きな値のキャラクタを持たないということだ。なぜならUTF-8でそれは認められていないから。
utf8
フラグはつまり、2つのモード間のスイッチなのである:スイッチオフはPerl内でUnicode文字列を得るということだし、スイッチオンはPerl内でUTF-8でエンコードされたオクテット/バイナリを得るということだ。
□latin1
あるいはascii
オン
latin1
(またはascii
)をオンにすると、encode
は255より大きい(ascii
の場合は127より大)文字コード値を持つキャラクタをエスケープし、かつ、それ以外のキャラクタはutf8
フラグの指定通りにエンコードする。
もしutf8
がオフなら、出力結果はそれらのキャラクタセットで正しくエンコードされている(というのも、Perl内部では全ての構成キャラクタが256未満のUnicode文字列はISO-8859-1文字列と同じものだし、全ての構成キャラクタが128未満のUnicode文字列はASCIIと同じものであるという意味で、どちらもUnicodeのサブセットとして適切だから)。
utf8
がオンであるなら、これらのフラグに関係なく正しくUTF-8でエンコードされた文字列を得る。先ほどのときよりも"\uXXXX"でエスケープされるキャラクタが増えるであろうが。
ASCIIエンコード文字列がUTF-8エンコーディングと互換性があるのに対し、ISO-8859-1「エンコード」文字列は互換性が無いことに注意。これは、ASCIIエンコードがUTF-8のサブセットであるのに対し、ISO-8859-1エンコードがそうではないからだ(ISO-8859-1「コードセット」はUnicodeのサブセットあるにも関わらず)。
驚くべきことに、decode
はこれらのフラグを無視する。それゆえ、全ての入力値はutf8
の影響下でとり扱われる。utf8
がオフならば、ISO-8859-1およびASCIIエンコード文字列はどちらもUnicodeの厳密なサブセットとしてdecodeできる。もしオンならば、UTF-8エンコード文字列を正しくdecodeできるだろう。
だから、latin1
もascii
もutf8
フラグと関係はない。それらはただJSONデータの出力部【encode】が、あるキャラクタをエスケープするかしないかを決定する。
latin1
フラグは、バイナリデータを比較的効率よくJSONとして保存するために主に利用される。これはほとんどのJSONデコーダーとの互換性を破壊するという犠牲を伴う。
ascii
フラグは、出力結果が127より大きい値を持つキャラクタを含まないよう強制するために主に利用される。このことは、encode
から出力された文字列をUTF-8, ISO-8859-1, ASCII, KOI8-R、あるいはほとんどのキャラクタセット及び8bitエンコーディングとして解釈可能であること、そして逆に同じデータ構造を【decodeで】得られることを意味する。これはあなたが利用するJSONデータ転送チャンネルが8-bitクリーンでないとき、あるいはそのエンコーディングが両者間で文字化けを起こすかもしれない場合(例えば電子メール)に役に立つ。ASCIIは、世界で利用されているほとんどの8-bitエンコーディング、あるいはマルチバイトエンコーディングの適切なサブセットなので、これはうまくいく。
(JSON::XS/ENCODING/CODESET_FLAG_NOTES:【】は訳者追加部分)