[Perl] 5.10.0 で pack・unpack の仕様が変わった『U0C*』とか
日本語・中国語・韓国語の文字列をローマ字読み下しする Lingua::*::Romanize::* 系モジュール群を
Google Code に移動したので、この週末に久しぶりにバージョンアップを実施。
従来は、UTF-8 フラグ OFF のバイト列 UTF-8 コードにのみ対応していたのを、
UTF-8 フラグ ON の文字列と OFF のバイト列のどちらも透過的に扱うように改良。
しかし、CPAN Testers からエラーの報告が。
Perl 5.10.0 では、unpack() の仕様が変わっていたらしい。
Packing and UTF-8 strings - Incompatible Changes
要は、UTF-8 フラグ ON の文字列の場合の pack() と unpack() 動作が変更になった。
従来の動作を再現するには、use bytes を使う。
あるいは、C0 を指定すると文字列モード、U0 を指定するとバイトモードになる。
C0・U0 は以前の pack()・unpack() では無視されていたので、互換性も◎。
pack - Perl 5.10.0 documentation
マニュアルにも C が octet で W が char と書いてある。(逆?)
以下のコマンドで動作確認。
UTF-8 フラグ ON の文字(\x{6F22}=漢)および
UTF-8 フラグ OFF のバイト列(\xE6\xBC\xA2=漢)の C* 分解数を表示する。
バイト単位なら 3 バイト、Unicode 文字単位なら 1 文字が返るはず。
実際の動作をまとめると、以下の通り。
UTF-8 フラグ ON の場合、"C*" が文字数をカウントしてる。(1)
つまり、従来の UTF-8 フラグ ON のときの pack "C*" の動作を再現するには、
先頭に U0 を使う(=use bytes 相当)か、W* を使う必要がある。
ただし、U0、W*、use bytes は Perl 5.005 では利用できない。
また、UTF-8 フラグ OFF の場合に U0C* を使うと、Latin-1 扱いされてるのか、壊れてる?(6)
今回、unpack() のエラーが発生した Lingua::JA::Romanize::Kana は、
できればまだ Perl 5.005 対応も継続したいので、
とりあえず UTF-8 フラグを OFF にしてから unpack するように変更する予定。
PS)
UTF-8 フラグは気にしなくて済むようにできるだけ透過的に扱いたいのだけど、
Perl 5.005 と Perl 5.10.0 両対応のプログラムを組むのは、ますます面倒になってきた。
Google Code に移動したので、この週末に久しぶりにバージョンアップを実施。
従来は、UTF-8 フラグ OFF のバイト列 UTF-8 コードにのみ対応していたのを、
UTF-8 フラグ ON の文字列と OFF のバイト列のどちらも透過的に扱うように改良。
しかし、CPAN Testers からエラーの報告が。
Perl 5.10.0 では、unpack() の仕様が変わっていたらしい。
Packing and UTF-8 strings - Incompatible Changes
The semantics of pack() and unpack() regarding UTF-8-encoded data has been changed. Processing is now by default character per character instead of byte per byte on the underlying encoding. Notably, code that used things like pack("a*", $string) to see through the encoding of string will now simply get back the original $string. Packed strings can also get upgraded during processing when you store upgraded characters. You can get the old behaviour by using use bytes .
To be consistent with pack(), the C0 in unpack() templates indicates that the data is to be processed in character mode, i.e. character by character; on the contrary, U0 in unpack() indicates UTF-8 mode, where the packed string is processed in its UTF-8-encoded Unicode form on a byte by byte basis. This is reversed with regard to perl 5.8.X, but now consistent between pack() and unpack().
要は、UTF-8 フラグ ON の文字列の場合の pack() と unpack() 動作が変更になった。
従来の動作を再現するには、use bytes を使う。
あるいは、C0 を指定すると文字列モード、U0 を指定するとバイトモードになる。
C0・U0 は以前の pack()・unpack() では無視されていたので、互換性も◎。
pack - Perl 5.10.0 documentation
c A signed char (8-bit) value.
C An unsigned char (octet) value.
W An unsigned char value (can be greater than 255).
マニュアルにも C が octet で W が char と書いてある。(逆?)
以下のコマンドで動作確認。
perl -e '@a = unpack("C*","\x{6F22}"); print scalar @a, "\n";'
perl -e '@a = unpack("C*","\xE6\xBC\xA2"); print scalar @a, "\n";'
UTF-8 フラグ ON の文字(\x{6F22}=漢)および
UTF-8 フラグ OFF のバイト列(\xE6\xBC\xA2=漢)の C* 分解数を表示する。
バイト単位なら 3 バイト、Unicode 文字単位なら 1 文字が返るはず。
実際の動作をまとめると、以下の通り。
UTF-8 flag ON (char) | UTF-8 flag OFF (octet) | |||||||
C* | U0C* | W* | use bytes C* | C* | U0C* | W* | use bytes C* | |
Perl 5.10.0 | 1 | 3 | 3 | 3 | 3 | 6 | 3 | 3 |
Perl 5.8.8 | 3 | 3 | N/A | 3 | 3 | 3 | N/A | 3 |
Perl 5.6.2 | 3 | 3 | N/A | 3 | 3 | 3 | N/A | 3 |
Perl 5.005 | N/A | 3 | N/A | N/A | N/A |
UTF-8 フラグ ON の場合、"C*" が文字数をカウントしてる。(1)
つまり、従来の UTF-8 フラグ ON のときの pack "C*" の動作を再現するには、
先頭に U0 を使う(=use bytes 相当)か、W* を使う必要がある。
ただし、U0、W*、use bytes は Perl 5.005 では利用できない。
また、UTF-8 フラグ OFF の場合に U0C* を使うと、Latin-1 扱いされてるのか、壊れてる?(6)
今回、unpack() のエラーが発生した Lingua::JA::Romanize::Kana は、
できればまだ Perl 5.005 対応も継続したいので、
とりあえず UTF-8 フラグを OFF にしてから unpack するように変更する予定。
PS)
UTF-8 フラグは気にしなくて済むようにできるだけ透過的に扱いたいのだけど、
Perl 5.005 と Perl 5.10.0 両対応のプログラムを組むのは、ますます面倒になってきた。
この記事へのコメント