Perl 5.8 & I18N

最近、久々にPerlを使い、バージョン5.8を使ったのですが、昔のPerlと比べるとずい分と変わっているところも多く、文字列まわりでちょっと混乱し、ハマってしまったので、わかっている人には無駄な情報と思いますが、Perl 5.8の初心者の人等、それなりに役に立つこともあるかと思いまとめてみました。

例えば、下記のコードは、いわゆる日本語環境下では、何の問題もなく期待した通りに「日本語」という文字列が表示されます。

$string = "日本語";
print $string;


で、普通はこれでいいように思うのですが、例えば、UTF-8でファイルに書き出したいと思って、下記のようなコードを実行し、作成されたtest.txtファイルを開いて見ると、見事に文字化けします。


open (OUT, ">:utf8", "test.txt") || die "Can't open test.txt: $!\n";
$string = "日本語";
print $string;
print OUT $string;
close OUT;

私の場合、英語版のWindows XPで日本語ロケールを設定して使っているので、そのためにこういう問題が起きるのかと最初に思ってしまいました。特に、ほぼすべてのアプリケーションで日本語を表示できるようにするために、システムロケールは日本語にしてあるのですが、米国での使用を前提としたアプリケーションも同時に使うためにユーザロケールは英語(米国)に設定しているので、余計にそう思ったのですが、すべて日本語ロケールに設定しても、日本語版のWindows 2003で試しても同様の結果でしたので、Perl自体の実装の問題(というか仕様)であると思います。

この環境下でも上記の何の変哲もないコードは正常に実行され、「日本語」という文字列が表示されます。しかし、それはたまたまであって内部的には、実はガベージイン・ガベージアウトが行われているのです。Javaのようにソースコードファイルがどのエンコーディングかを指定できるようにするのが本来は筋なのだと思うのですが、Perlの場合は、そういう指定ができるのはutf8を使うかどうかという指定(use utf8を頭に書く)だけが存在し、将来はUTF-8で一本化する方針のため(use utf8もなくす)、シフトJIS等でソースを作成することは正式にはサポートしないという立場のようです。で、上記のようにシフトJISで保存したファイル内のリテラル文字は、適当なバイト列として内部的には扱われてしまうようで(UTF-8になっているわけでもない様子)、文字列に対する操作やコード変換を伴う入出力を行わない限りは、その事実に気付かないということになります。

この場合、主に2つの方法があります。一つ目は、use utf8を宣言して、ファイル自体をUTF-8で保存し、Perl外部との入出力には適切なエンコーディング情報を指定してあげる方法です。コードは、大体次のようになります。

use utf8;

open (OUT, ">:utf8", "test.txt") || die "Can't open test.txt: $!\n";
binmode(STDOUT,":encoding(shift_jis)");

$string = "日本語";
print $string;
print OUT $string;
close OUT;

use utf8と書いても、単にソースコードファイルのエンコーディングにUTF-8を使っていることをPerl実行系に伝えるだけですので、ファイルの入出力やコンソールの入出力には、これとは別に都度エンコーディング情報を適切に指定する必要があります。例えば、上記のコードで、

open (OUT, ">test.txt") || die "Can't open test.txt: $!\n";

としてしまうと、ファイルに出力する時点で、

Wide character in print at testutf8.pl line 8.

のように警告が発せられます。(この場合は結果的にはUTF-8で表現されたバイト列が書き出されるので、UTF-8でファイルが出力されることにはなりますので実害はありませんが。)

2つ目の方法は、Encodeモジュールを使用して、入出力にエンコーディングを指定することに加えて、リテラル文字列には、decodeやencodeをいちいち施してやるという方法です。

use Encode;

binmode(STDOUT,":encoding(shift_jis)");
open (OUT, ">:utf8", "test.txt") || die "Can't open test.txt: $!\n";

$string = "日本語";
$string = decode("shift_jis", $string);
print $string;
print OUT $string;
close OUT;

どちらの方法の方がいいとは言えませんが、Perlコミュニティが目指している方向が、UTF-8のデフォルト化のようですので、1つ目の方法で作成して、Encodeモジュールを必要に応じて使用するというのが良さそうです。

PerlのUnicode対応に関する、もっと大きなまとめは下記のページが一番よくまとまっていて信頼できそうなので、そちらを読んで理解を深めてください。
http://www.lr.pi.titech.ac.jp/~abekawa/perl/perl_unicode.html

This entry was posted in コンピュータ and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *