2009年06月15日 07:00 [Edit]

perl - use utf8; #って何だ?

camel

id:otsuneに建設予定フラグがたてられていたので。

冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech
Perl の utf8 関係が未だ全く理解できない。わからないことがわからないので整理

use utf8はいつフラグをたてるか

use utf8 しててもフラグたたないことがある……

これは、以下の実例を見ていただくのが一番よいだろう。

#!/usr/bin/perl
use strict;
use warnings;
use utf8 ();

sub check_flag{
    my $str = shift;
    print qq("$str" ), utf8::is_utf8($str) ? 'is' : 'IS NOT', " flagged.\n";
}

{
    use utf8;
    check_flag("dankogai");
    check_flag("小飼弾");
    check_flag("dankogai" . "小飼弾");
    check_flag("小飼弾" . "dankogai");
}
{
    check_flag("dankogai");
    check_flag("小飼弾");
    check_flag("dankogai" . "小飼弾");
    check_flag("小飼弾" . "dankogai");
}

最初のcheck_flag("dankogai");が、use utf8;の影響化にあるにも関わらず、utf8フラグを立てない例である。

なぜ立てないかというと、dankogaiは全てASCII領域にあるため、立てた場合も立てない場合も文字列処理の結果が同じになるからだ。

utf8プラグマは、

  • ASCII領域外の文字を含むリテラル、すなわち/[^\x00-\x7f]/にマッチするリテラルを、UTF-8化する

と覚えておくとよい。

encode/decodeの覚え方

これはそれほど難しくない。言語の側から見て

  • バイト列を内部表現化するのがdecode
  • 内部表現をバイト列化するのがencode

もっと単純に、

  • 入力はdecode
  • 出力はencode

でも構わない。

なお、この原則は、PerlのみならずPythonやRuby (1.9以降)でも通じる。

相手がutf8フラグを考慮していない場合

海外製のだいたいの CPAN ライブラリのこと

こういう場合の理想は、相手にそのことをtestとpatch付きで通知することだろう。そしてそれがapplyされるまでは、こちら側で対処しておく。このあたりのことは以前

にも書いたのでそちらも参照のこと。

たしかにこの正攻法は手間も暇もかかるが、そうした地道な努力の積み重ねが、この問題そのものの啓蒙にもつながるし、実際状況はPerl 5.8.0がリリースされた頃と比べれば格段によくなっている。

正規表現はどうなる?

正規表現マッチを正しくさせるには?

そのためにこそ、UTF-8 Semantics が存在すると言っても過言ではない。それ以前は文字列はバイト列としてしか扱えなかった。逆に言えば「リテラルをそのまま出力」するような「バイト列で済む」用途であればUTF-8フラグは不要だったのだ。

しかし、バイト列のまま扱ったのでは、以下のような例が出てしまう。

print "カタカナ" =~ /織/; # EUC-JP だと マッチ!

つまり、こういうことである。

|カ    |タ      |カ     |ナ  
\xa5\xab\xa5\xbf\xa5\xab\xa5\xca
            |織     |

UTF-8 Semantics ではこういうことは起こりようがない。

で、文字化けはなくなるのか

残念ながら、全てをなくすことは無理である。Perlが悪いのでも、あなたが悪いのでもない。文字化けするかどうかはユーザーの環境が決めているからだ。たとえば現代のブラウザーのほぼ全てが、文字コードを強制指定する機能を持っているが、そこでShift_JISを指定されたら、こちらがいくらcharset:UTF-8と主張しても無駄である。

しかし、なるべく文字化けを起こさないように誘導することは可能である。HTTPならcharset指定を省かないこと、新規のプロジェクトの場合ははじめからUTF-8を使うことなどである。

ここでもまた、地道な努力の積み重ねが利いた事例で、昔に比べると文字化けクレームは驚くほど減っている。SafariのEUC-JPの円マーク問題のように、なかなかゼロに出来ないのも悲しいことではあるが。

いずれにせよ、状況はゆっくりではあるが改善している。UTF-8は技術的なベストではないし、Unicodeには言いたいことはいくらでもあるが、以前の状況のひどさはこんなものではなかった。私としては、文字化けがなくならない状況にじたんだを踏むより、「パソコンおたく」でなくても文字コードをほとんど気にせず文字を扱えるようなった今の状況を寿ぎ、そのの一助となれたことを素直に喜びたい。

Dan the Encode Maintainer


この記事へのトラックバックURL

この記事へのコメント
ラクダ本第4版を待つ間「まるごとPerl! Vol.1」で代用するといいでしょう。

encode/decode はスクリプトの実行時に働くものなので、つまりはプログラマがデバッグする必要があります。ネーミングからしてそうなのですが、言語処理系のためにプログラマが働かされているようなイメージがあります。

一方で、先祖帰りかもしれませんが Jcode であれば、プログラマが何をしたいのかを

# 古式ゆかしく
Jcode::convert($str, $ocode, $icode, "z");

# もしくはオブジェクト指向で!
print Jcode->new($str)->h2z->tr($from, $to)->utf8;

のように書けるので大変便利です。

> Perl の utf8 関係が未だ全く理解できない。

理解できないのはあなたのせいでも、あなただけでもありません。
問題は「utf8 関係」の側にあるのです。
Posted by スルーできず書き込み at 2009年06月25日 09:34
日本語で書かれたPerlの本でEncodeをきちんと解説したのが1冊もないのがまずいのでは…
ラクダ本第4版を待つしかないのか。
Posted by 挧 at 2009年06月15日 18:32
> Ruby (1.9以降)でも通じる
Rubyは1.9でも内部表現がバイト列なCSIなので違います。ちなみにString#encodeはあるけどString#decodeはないよ。

> SafariのEUC-JPの円マーク問題
誰かやつを説得してくれ・・・
Posted by 成瀬 at 2009年06月15日 08:28