CPAN にあった Perl の Unicode Tutorial を和訳したものです。
原文: perlunitut - Perl Unicode Tutorial
perlunitut - Perl Unicode Tutorial
Perl の Unicode サポートに関するチュートリアル
「文字」というものを無視していた状況は過去のものとなりました。現代のプログラムはアクセントつきの文字やユーロ記号のようなもので情報伝達する能力が必要であるという事実が認められました。つまり、プログラマは新たな習慣を身に着けなければなりません。Unicode を扱えるソフトウェアをプログラミングするのは簡単ですが、正しくプログラムを書くためには鍛錬が必要です。
文字集合 (character sets) とテキストエンコーディング (text encodings) について、たくさん学ばなければなりません。これら全てを学ぶのに丸一日かけるのもいいですが、基本はすぐに学ぶことが出来ます。
けれど、あまりにも基本から進めるということはありません。この文書の読者は既にバイト列と文字列の違いや、文字集合とエンコーディングの違いをはっきりわかっていて、これらを明確に区別してプログラムを書ける、ということを想定しています。これらがまだ不確かな方は、Joel Spolsky によって書かれた "The Absolute minimum Every Software Developer Absolutely, Positively Must Know About unicode and Character Sets (No Excuses!)" を読んでください。
(訳者注: Perl の pack / unpack 関数がどういうことをしているのか、URL エンコードとは何なのかということがわかってればいいと思います。)
このチュートリアルでは、どちらかというと原則的な部分について述べ、文字列に関する様々な観点のうち、Perl が提案する将来像に関係する部分的な見方だけを提供します。
まず、いくつかの事象をきちんと定義しておかなくてはなりません。この定義が、このチュートリアルで最も重要な部分です。以下で定義するものは、Web 上であなたが今まで見てきた情報と矛盾があるかもしれませんが、その矛盾のほとんどが Web 上の情報の元となった情報が誤っているために生じています。
この節全体を何度か読み返してみることをおすすめします。
ユニコード (Unicode) は多くの文字の種類で区切られた文字集合です。順番に番号が与えられており、その数字をコードポイント (code point) と呼びます。
多くのコードポイントがありますが、コンピュータは byte でデータを扱い、1 byte はたった 256 種の値しか取れません。Unicode は当然 256 以上の文字を扱うので、Unicode 文字をコンピュータが扱いやすいようにする方法が必要になります。
その方法がエンコードで、Unicode には様々なエンコード方式があります。UTF-8 はエンコード方式の 1 つで、広く使われています。Unicode のエンコードでは、(単一の)コードポイント(つまり 1 文字)を表すために 1 byte だけでなく 2 bytes 以上のバイト列を使うことができます。
UTF-8 は Unicode のエンコーディング方式の 1 つです。多くの人が Unicode と UTF-8 を同一のものと思っているようですが、それは誤りです。Unicode のエンコーディング方式は他にもあります。ただ、標準的に使われているのは UTF-8 です。
UTF-8 では、最初の 128 個のコードポイント (内部コード: 0 ~ 127) は ASCII と同じコードになっています。この部分は 1 文字に 1 byte しか使いません。他の部分は全て、複雑な変換方式によって 2 bytes かそれ以上(最高 6 bytes)のバイト列にエンコードされます。恵まれたことに、この複雑な変換は Perl が行ってくれます。だから変換に関して心配することはありません。
文字列 (text strings または character strings) は文字によって成るものです。バイトやエンコーディングとは無関係です。文字はただ文字でしかありません。
文字列は Unicode 列 (Unicode strings) とも言います。それは Perl 内部で全ての文字列は Unicode の文字の列で扱っているからです。
文字列を扱っている時、次のようにすることが出来ます。
$text =~ s/foo/bar/;
if($string =~ /^\d+$/) { ... }
$text = ucfirst $text;
my $character count = length $text;
文字の値 (ord 関数や chr 関数で引数に渡す値) は Unicode のコードポイントに対応するものです。
(訳者注: Unicode コードポイントは 16 進数で表すのが普通ですが、ord / chr 関数に渡す時は 10 進数にしなければなりません。)
バイナリ列 (binary strings または byte strings) はバイトによって成るものです。文字ではなくバイトです。外部 (動いている 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;
エンコード (Encoding (as a verb)) とは、テキスト (text) からバイナリ (binary) に変換することです。エンコードするには、変換後のエンコーディング(エンコード方式)を指定しなければなりません。例えば、iso-8859-1 や UTF-8 などです。iso-8859 ("latin") の範囲のように、Unicode 標準の文字をカバーしきれないエンコード方式もあり、表現できない文字は変換時に失われてしまいます。
デコード (Decoding) は、バイナリ (binary) からテキスト (text) に変換することです。デコードするには、デコード対象のバイナリ列がエンコード時にどんなエンコード方式を使ったのかがわからなければなりません。ほとんどの場合デコード可能ですが、PNG 画像を文字列にデコードするなんてことは論外です。
Perl は、文字列 (text strings) をメモリに記録するためのエンコード方式として、内部形式 (Internal format) というものを使います。全ての文字列 (text strings) はこの内部形式によって表されます。もっと言えば、文字列 (text strings) は内部形式以外で表されることは決してありません!
この内部形式 (internal format) がどのような形式なのか、気にする必要はありません。変換は encode 関数や decode 関数によって自動的になされます。
プログラムのヘッダ部に次の行を追加してください。
use Encode qw(encode decode);
面倒くさければ次のように書くだけでもいいでしょう。
use Encode;
これで encode 関数、decode 関数が使えるようになります。
プログラムの典型的な入出力は以下のようになります。
1. 受信し、デコードする 2. 処理を行う 3. エンコードし、出力する
入力がバイナリで、バイナリのまま処理すべきと考えられるならば、もちろん文字列 (text strings) にデコードする必要はありません。が、それ以外の場合は全てデコードした方がいいでしょう。
デコード対象がどんなエンコード方式でエンコードされているかがわからなければ、デコードが確実にできるわけではありません。エンコード方式がわからないとき、標準的に 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'));
あなたが気づく間もなく処理は終わっています。これで、バイト (bytes) としてではなく文字 (characters) として扱えるようになりました。substr 関数や length 関数がずっと使いやすくなるでしょう。
文字列 (text strings) にはバイト (bytes) が含まれていない、ということは重要なことです。もちろん、Perl は記憶領域に文字を置くために内部的なエンコード方式(内部形式)を使いますが、それは無視してください。もしもバイト (bytes) として処理を行わなければならないならば、3 つめのステップに移ってエンコードした後に行うのが一番良い方法でしょう。その時、まさしくあなたは目的のバイト列がいかに多くのバイトになるかを知るでしょう。
(訳者注: ここら辺いまいち意味がわかりません;)
文字列 (text strings) からバイナリ列 (binary strings) にエンコードする構文は、デコードと同様単純です。
$body = encode('UTF-8', $body);
(文字数ではなく)バイト長を知りたいならば、ここで行ってください。今 $body はバイト列になっているので、length 関数は文字数ではなくバイト長を返します。もはや文字数はわかりません。文字はただ文字列 (text strings) にしか存在しないのですから。
my $byte_count = length $body;
使っているプロトコルが、使用した文字エンコード方式を受信側が知る方法をサポートしているならば、その機能を使って受信側を手伝ってください。例えば、E-mail と HTTP は MIME ヘッダをサポートしているので、Content-Type ヘッダを使うことが出来ます。同時にバイト数を知らせる Content-Length も使うことができ、バイト数がわかっているかどうかを教える良い方法です。
"Content-Type: text/plain; charset=UTF-8", "Content-Length: $byte_count"
受信したものはデコードし、出力するものはエンコードする。
ただし、それがテキストデータである場合に限る。
この文書を読んだ後、perlunifaq を読むことをおすすめします。
Tutorial の内容と直接関係がないため訳は省略します。
原文: perlunitut - Perl Unicode Tutorial
perlunifaq,
perlunicode,
perluniintro,
Encode