今日のCPANモジュール
use Encode; (ADVANCED)
ここは本編への補足や本編で取り上げなかった話題について置いておく場所です。
« 本編へ戻る
だいたい同じと乱暴にまとめた latin-1 の話
latin-1 とは俗称で、正式には ISO-8859-1 とか言う。おおざっぱに言うと ascii 127 文字の続きに1バイトに収まる範囲でアクセント記号付きアルファベットとかを付け加えたもの(表)。付け加えるもののバリエーションで latin-2(ISO-8859-2)とかあったりする。
Perl は utf8 フラグのなかった時代のコードもそのまま動くよう、latin-1 の範囲(1バイト = 10進で255以下、16進で xFF 以下)を優遇しその範囲は Encode::decode() なしにフラグが立たないようにしている。(例えば "x{UUUU}" や chr 0xUUUU 表記)
なので Data::Dumper で "x{UUUU}" 表記になるか見るだけで utf8 フラグが立っているかどうかを判断するのは実は正確ではない。日本語(非1バイト)を扱っている場合はだいたいそれでわかりますが。
この辺は主に入出力を担当するモジュールを書く人が気をつけるべき話題で、モジュールを作る人はできるだけ利用者が Unicode だけを使えばよいようにしてあげよう。ということで本編では流した。モジュールでの Unicode 利用の歴史: 1, 2, 3
自身でフラグを気にしなければいけない場合は Data::Visitor::Encode が便利。
まったく登場しない utf8:: の話
utf8::decode() は Encode::decode() と違い latin-1 の範囲はフラグを立てないのでその差に注意しなければならない。あと日本人はどうせ utf8::* だけでは済まないことが多い。なので utf8:: ではなく Encode::* 推奨とした。
同様の理由で Catalyst でエンコーディングが UTF-8 だけの場合は使えるのだけど C::P::Unicode は割愛した。
まったく登場しない encoding プラグマの話
非推奨であるのとソースは UTF-8 を使えるケースが多いと思うのでスルー。
まったく登場しない PerlIO の話
Perl には PerlIO というファイルハンドル動作のラッパー機能があり、これを使ってファイル open や print の時 自動で decode()/encode() させることができる。
PerlIO は便利だけどこれ専用の機能ではないし Encode::decode()/ Encode::encode() で済むといえば済むので、よくわからないうちは無理に使わなくても良いと思いスキップ。あくまでも便利機能という位置づけとした。
ここで簡単に触れておきます。PerlIO の使い方は3パターン。それぞれ例を示します。
open プラグマによる指定
use open qw/:utf8 :std/; open(my $fh, '<', 'file.txt') or die $!; while (my $line = <$fh>) { print $line; }これは以下とだいたい同じです。
use Encode; open(my $fh, '<', 'file.txt') or die $!; while (my $line = <$fh>) { $line = decode('utf-8', $line); print encode('utf-8', $line); }open()(3つ引数取る版)の第2引数に仕込むopen(my $fh, '<:encoding(euc-jp)', 'file.txt') or die $!; while (my $line = <$fh>) { print $line; }これは以下とだいたい同じです。
use Encode; open(my $fh, '<', 'file.txt') or die $!; while (my $line = <$fh>) { $line = decode('euc-jp', $line); print $line; }binmodebinmode STDOUT, ':utf8'; print "x{7B11}";これは以下とだいたい同じです。
print encode('utf-8', "x{7B11}");
と、decode()/encode() を書かずに透過的に decode()/encode() できます。PerlIO レイヤは :utf8 や :encoding() タグ以外にもいろいろあったりします。入出力のラッパーだと認識した上で使うと便利です。
cp932 の話
Encode の Shift_JIS エンコーディングは純粋な Shift_JIS。Microsoft が Shift_JIS を拡張して入れた ①㈱ みたいな文字を含む Shift_JIS は cp932 というエンコーディングで別に入っている。機種依存文字も最近は Mac でも見れたり入力できたりするので、cp932 をつかっておけばよい、とした。
この話題についてはLegacy Encoding Project に詳しい。shift_jis に対する cp932 と同じようなポジションで Encode::EUCJPMS や Encode::ISO2022JPMS(開発中?)がある。
波ダッシュ/全角チルダ問題
charset=Shift_JIS な HTML からキーボードから普通に入力した「~」は、shift_jis で decode() すると U+301C に、cp932 では U+FF5E にマッピングされる。さらに、charset=UTF=8 な HTML からは Windows と Mac で違うのが来て、utf-8 で decode() すると Windows からのは U+FF5E に、Mac からのは U+301C にマッピングされる。
問題は encode() 時で、shift_jis には U+FF5E に対応するのがない。まあ shift_jis には機種依存文字もないので良いが、cp932 は機種依存はあるが U+301C に対応するのがないので ? になる。
といっても実際問題になるケースは少なく、Mac から decode('utf-8') したのを encode('utf-8') して保存しておいて、場合によっては decode('cp932') で出すというケースとかだが、Encode::FB_HTMLCREF を使うのは嫌でどうしても出したい時は
# cp932 を使って encode() する場合。shift_jis でやる場合は逆にする
$text =~ s/x{301C}/x{FF5E}/g; # WAVE DASH to FULLWIDTH TILDE
print encode('cp932', $text);
みたいに置換してから encode() という方法がある。
しかしこれって「PC/携帯 でデータを共有するサービス」が想定されるケースであり、携帯に特化したエンコーディングパックを追加する Encode::JP::Mobile の shift_jis 系エンコーディング(x-sjis-docomo)とかは cp932 ベースだが U+301C も特別に入っているので置換とかする必要がないので普通にこれを使えば良い。(今のところ波ダッシュ以外の違う奴はサポートしていないが)
その他、Unicode / Encode に関係あるけど割愛したもの
そのスカラに utf8 フラグが付いているかどうかの以下のようなチェックの件。よく考えると実際調べてどうするというケースが多いので
warn warn use
pack()の U- charnames の
N{名前}。便利そうだけど微妙に使う機会少ないんだよな - 当方 EBCDIC 環境(オフコン?)は知りませんのでよろしく
正規表現
- 正規表現の
p{プロパティ}は便利。だけど長くなるので Unicode::UCD, Unicode::Normalize と合わせて別エントリにしようか dが全角にもマッチするから FormValidator::Simple では'ASCII'とセットにすると良いよ、というのは FormValidator::Simple 取り上げる時書けば良いか
Encode
decode_utf8($text)はdecode('utf-8', $text)に同じ、
encode_utf8($text)はencode('utf-8', $text)に同じ。クオート符号が少なくという利点があるのでたまに使いますfrom_to()は、名前もイマイチだしバイト時代の人向け- 自前エンコーディングを作れる機能は、Encode::JP::Mobile とか作る人向け。
define_alias()は、sjis って書いちゃったやつを全部 cp932 にしたい時とか便利。でもまあ普通に cp932 って書けばいいかと- CHECK にコールバックを仕込むのは実は使いこなすのは大変なので詳しくはパス
- Encode::JP::H2Z については Unicode::Japanese の方がよいと思うので別でとりあげることにする。
WRITTEN BY
冨田 尚樹 <tomita@cpan.org>