NAME

perlunitut - Perl における Unicode のチュートリアル

DESCRIPTION

文字列を単に放り出す日々は終わりました。 最近のプログラムでは変わったアクセントのついた文字や、ユーロのマークの ようなものを通信出来る必要があることが確立しています。 これは、プログラマは新しい意味が必要であることを意味しています。 Unicode 対応ソフトウェアをプログラムするのは簡単ですが、それを正しく行うには 訓練が必要です。

文字集合とテキストエンコーディングに関しては学ぶべきことがたくさんあります。 おそらくこれら全てを学ぶのに丸一日を使うのが最良ですが、基本は数分で 学べます。

しかし、これらは超基本ではありません。 あなたがすでにバイトと文字の違いを知っていて、様々な文字集合と エンコーディングがあることを自覚していて(さらに受け入れていて!)、 あなたのプログラムがこれらを明示的にする必要がある、と仮定しています。 お勧めの読み物は Joel Spolsky による "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)" http://joelonsoftware.com/articles/Unicode.html です。

このチュートリアルはどちらかといえば独立した用語を使っていますし、Perl が 提供している豊富な文字列に関する機能のうち限定された見方のみを扱っています。 ほとんどのプロジェクトにとっては、おそらくこの情報で十分です。

定義

まず、いくつかのことをはっきりさせることが重要です。 これはこのチュートリアルで最も重要な部分です。 この考え方はあなたが web で見つけたその他の情報と矛盾するかもしれませんが、 ほとんどの場合、それは web で見つけた情報の方が間違っているためです。

このセクション全体を何回か読み直す必要があるかもしれません…

Unicode

Unicode は大量の文字を表現できる文字集合です。 文字の序数は 符号位置 と呼ばれます。 (しかし実際には、符号位置と文字との区別はあいまいなので、この用語はしばしば 同義語として使われます。)

符号位置は本当に大量にありますが、コンピュータはバイト単位で 動いていて、バイトは 256 種類の値のみを持ちます。 Unicode はもっとたくさんの文字を持っているので、これらにアクセスできるような 手法が必要です。

Unicode はいくつかの競合するエンコーディングを使ってエンコードされていて、 UTF-8 がもっともよく使われています。 Unicode のエンコーディングでは、複数の引き続くバイト列を一つの符号位置 (あるいは単純に「文字」)を保管するのに使います。

UTF-8

UTF-8 は Unicode のエンコーディングです。 多くの人々は Unicode と UTF-8 は同じだと考えていますが、違います。 Unicode のエンコーディングには他にもありますが、世界のほとんどでは標準として UTF-8 が使われています。

UTF-8 は最初の 128 の符号位置(0..127) を ASCII と同様に扱います。 文字毎に 1 バイトしかかかりません。 その他の文字は、複雑な仕組みを使って 2 から 4 バイトを使います。 幸い、Perl がその作業をするので、私たちがそれについて心配する必要は ありません。

テキスト文字列(文字の文字列)

テキスト文字列、または 文字の文字列 は文字からなります。 バイト列はここでは無意味で、エンコーディングも無意味です。 各文字は単に: 文字です。

テキスト文字列では、以下のようにします:

    $text =~ s/foo/bar/;
    if ($string =~ /^\d+$/) { ... }
    $text = ucfirst $text;
    my $character_count = length $text;

文字の値 (ord, chr) は対応する Unicode 符号位置です。

バイナリ文字列(バイト文字列)

バイナリ文字列、または バイト文字列 はバイト列からなります。 ここでは、文字はなく、単にバイトだけがあります。 外側の世界(現在の Perl プロセスの外側のあらゆるもの) との通信はバイナリで 行われます。

バイナリ文字列では、以下のようにします:

    my (@length_content) = unpack "(V/a)*", $binary;
    $binary =~ s/\x00\x0F/\xFF\xF0/;  # for the brave :)
    print {$fh} $binary;
    my $byte_count = length $binary;

エンコード

(動詞としての) エンコードテキスト から バイナリ への変換です。 エンコードするには、iso-8859-1UTF-8 のような、エンコード先の エンコーディングが必要です。 iso-8859 ("latin") のような一部のエンコーディングは、Unicode 標準に 完全に対応していません; 表現できない文字は変換時に失われます。

デコード

デコードバイナリ から テキスト への変換です。 デコードするには、エンコード時に使われたエンコーディングが何かを知っている 必要があります。 そしてそもそも、それはデコード可能でなければなりません。 PNG イメージをテキスト文字列にデコードしてもほとんど意味はないです。

内部形式

Perl には 内部形式 と呼ばれる、テキスト文字列をメモリに格納できるように エンコードを行うために使うエンコーディングがあります。 全てのテキスト文字列はこの内部形式になります。 実際、テキスト文字列が他の形式になることは決してないのです!

この形式がどのようなものであるかを気にする必要はないはずです; なぜなら 変換はデコードやエンコードの際に自動的に行われるからです。

新しいツールキット

あなたの標準ヘッダに以下の行を追加します:

    use Encode qw(encode decode);

あるいは、あなたが怠け者なら、単に以下のようにします:

    use Encode;

I/O の流れ(実際の 5 分間チュートリアル)

典型的なプログラムの入出力の流れは以下のものです:

    1. 受信してデコードする
    2. 処理する
    3. エンコードして出力する

もし入力がバイナリで、バイナリのままにしておく必要があるなら、これを テキスト文字列にデコードするべきではありません。 しかし、それ以外の全ての場合では、デコードするべきです。

もしデータがどのようにエンコードされたか分からないなら、デコードには 信頼性がありません。 もしあなたが選択するのなら、UTF-8 に標準化するのはいい考えです。

    my $foo   = decode('UTF-8', get 'http://example.com/');
    my $bar   = decode('ISO-8859-1', readline STDIN);
    my $xyzzy = decode('Windows-1251', $cgi->param('foo'));

処理は今まで通りに行います。 唯一の違いは、バイトではなく文字を使うことです。 これは substrlength のようなものを使うときにとても便利です。

テキスト文字列にはバイト列はないということを自覚することは重要です。 もちろん、Perl は文字列をメモリに保管するために内部形式を使いますが、 これは無視してください。 もしバイト数を扱うような何かをする必要があるなら、おそらくその部分を ステップ 3 の、文字列をエンコードした直後に移すのが最良です。 そこで目的の文字列が何バイトになるのかが正確に分かります。

テキスト文字列をバイナリ文字列にするための文法はデコード時と同じぐらい 単純です:

    $body = encode('UTF-8', $body);

もし文字列のバイト数が必要なら、今がそれをする最良のタイミングです。 なぜなら $body はバイト文字列なので、length は文字数ではなく、 バイト数を返します。 文字というものはテキスト文字列でのみ存在するので、もはや文字数というものは わからなくなっています。

    my $byte_count = length $body;

そしてもしあなたの使っているプロトコルが、あなたが使ったエンコーディングを 相手に伝える方法に対応しているなら、その機能を使って受信側を助けてあげて ください! 例えば、E-mail と HTTP は MIME ヘッダに対応しているので、 Content-Type ヘッダが使えます。 バイト 数を示すための Content-Length もあるので、もしバイト数が わかっているなら、これをつけるのは常に良い考えです。

    "Content-Type: text/plain; charset=UTF-8",
    "Content-Length: $byte_count"

まとめ

受け取ったもの全てを decode して、送り出すもの全てを encode しましょう。 (扱っているのがテキストデータなら。)

Q and A (or FAQ)

この文書を読んだ後、perlunifaq と、それから perluniintro も 読むべきです。

謝辞

Thanks to Johan Vromans from Squirrel Consultancy. His UTF-8 rants during the Amsterdam Perl Mongers meetings got me interested and determined to find out how to use character encodings in Perl in ways that don't break easily.

Thanks to Gerard Goossen from TTY. His presentation "UTF-8 in the wild" (Dutch Perl Workshop 2006) inspired me to publish my thoughts and write this tutorial.

Thanks to the people who asked about this kind of stuff in several Perl IRC channels, and have constantly reminded me that a simpler explanation was needed.

Thanks to the people who reviewed this document for me, before it went public. They are: Benjamin Smith, Jan-Pieter Cornet, Johan Vromans, Lukas Mai, Nathan Gray.

著者

Juerd Waalboer <#####@juerd.nl>

SEE ALSO

perlunifaq, perlunicode, perluniintro, Encode