CPAN にあった Perl の Unicode Tutorial を和訳したものです。 ところどころ意訳であったり原文にない表現があったりします。 わかりづらい部分がありましたら原文を参照してください。
perlunitut - Perl Unicode Tutorial
Perl の Unicode サポートに関するチュートリアル
「文字」 というものを無視していた状況は過去のものとなりました。 これまで 「日本語」 や 「アクセントつきの文字」、「ユーロ記号のようなもの」 はバイトの組み合わせで表現されていましたが、これからは 「バイト列」 ではなく 「文字列」 として扱う必要がある、という事実が認められました。 つまり、プログラマは新たな習慣を身に着けなければなりません。 Unicode を扱えるソフトウェアをプログラミングするのは簡単ですが、正しくプログラムを書くためには鍛錬が必要です。
文字集合 (character sets) と文字符号化 (text encodings) について、たくさん学ばなければなりません。 これら全てを学ぶのに丸一日かけるのもいいですが、基本はすぐに学ぶことが出来ます。
とはいえあまりにも基本から進めるわけではありません。
この文書の読者は既にバイト (bytes) と文字 (characters) の違いや、文字集合 (character sets) と符号化方式 (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) と呼びます。
多くのコードポイントがありますが、コンピュータはバイトでデータを扱い、1 byte はたった 256 種の値しか取れません。 当然ながら Unicode は 256 以上の文字を扱うので、1 byte を 1 文字として扱うことは出来ません。 Unicode 文字をコンピュータが扱いやすいようにする方法が必要になります。
その方法がエンコード (文字符号化, encodings) というもので、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) とは、文字の連続によって成るものです。 一般生活で使用する文字であり、コンピュータ内部のバイトやエンコーディングとは無関係です。 文字はただ文字でしかありません。
Perl では、文字列のことを Unicode 列 (Unicode strings) とも言います。それは Perl 内部で全ての文字列は Unicode の文字の列で扱っているからです。
文字列を扱っている時、次のようにすることが出来ます。
# 文字の置換ができる. $text =~ s/foo/bar/; # 「数字の連続」というパターンとのマッチングができる. # 注意:「数字」というのはあくまでも「文字」. if( $string =~ /^\d+$/ ) { ... } # 1 文字目を大文字にする関数 ucfirst を使うことが出来る. $text = ucfirst $text; # 文字列に対して length 関数を使うと、文字数を得られる. my $character_count = length $text;
文字の値 (ord 関数や chr 関数で引数に渡す値) は Unicode のコードポイントに対応するものです。
(訳者注: Unicode コードポイントは 16 進数で表すのが普通ですが、ord / chr 関数に渡す時は 10 進数にしなければなりません。)
バイナリ列 (binary strings または byte strings) はバイトの連続によって成るものです。 文字ではなくバイトです。 外部 (動いている Perl プロセスの外部) との通信は全てバイナリによって行われます。
バイナリ列を扱っている時、次のようにすることが出来ます。
# バイナリ構造体 (バイナリ列) にパックされた数値を unpack 関数で取得することができる. my(@length_content) = unpack "(V/a)*", $binary; # バイトの置換ができる. $binary =~ s/\x00\x0F/\xFF\xF0/; # for the brave :) # バイナリ列ならば外部 (今の場合はファイル) に出力できる. $fh はファイルハンドル. print {$fh} $binary; # バイナリ列に対して length 関数を使うと、バイト長を得られる. my $byte_count = length $binary;
エンコード (Encoding (as a verb)) とは、テキスト (text) からバイナリ (binary) に変換することです。エンコードするには、変換後のエンコード方式 (encoding) を指定しなければなりません。例えば、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 の内容と直接関係がないため訳は省略します。
原文: Juerd Waalboer <#####@juerd.nl>
和訳: nobuoka
原文: perlunitut - Perl Unicode Tutorial
perlunifaq,
perlunicode,
perluniintro,
Encode