blog.鶯梭庵

二〇〇七年 師走 十九日 水曜日

Perl で Jcode を使わずに日本語を扱う [/this_blog/perl]

Perl で日本語を扱う場合、ソースを EUC-JP または UTF-8 で書いて Jcode を使うというのが一般的だが、バージョン 5.8.1 以降であれば、Perl は実用に十分な程度に Unicode に対応しているので、Jcode を使う必要がない。

普通に $language = "日本語"; と書いた場合、あるいは外部から文字列を読み込んだ場合、Perl はこれをバイト(オクテット)の列として扱う。ソースが EUC-JP でエンコードされていれば 0xC6 0xFC 0xCB 0xDC 0xB8 0xEC だし、UTF-8 でエンコードされていれば 0xE6 0x97 0xA5 0xE6 0x9C 0xAC 0xE8 0xAA 0x9E だ。そのため、print length $language; と書いたら、EUC-JP の場合は 6 と出力されるし、UTF-8 なら 9 と出力される。

しかし、encoding プラグマを使うことで、Perl は文字列を正しく文字(キャラクター)の列として扱うようになる。たとえば、以下のソースを EUC-JP で書くことができる。


use encoding 'EUC-JP';

$language = "日本語";

print length $language; # -> 3

$language =~ s/^../英/;

print $language; # -> 英語


encoding を使うことで何が起こるかというと、スクリプトファイル内にハードコードされた文字列や標準入力からの入力が、自動的にバイト列から Unicode のキャラクター列へと変換され、Perl の内部形式へとデコードされる。そして、標準出力に出力するときには、自動的にバイト列へとエンコードされる。(ちなみに、入力と出力に異なる文字コードを指定することもできるので、ファイルを読み込んでそのまま出力するだけで文字コードの変換をすることができる。)

しかし、CGI などでユーザから入力を受け付けるときは、これではうまくない。ユーザが、こちらが指定した文字コードで入力してくれると前提するわけにはゆかないからだ。ユーザからの入力は決して信用しないというのが、アプリケーションを作るときの大原則である。

そこでどうするか考えてみた。まず、入力時の自動デコードを停止して、入力をバイト列として受け取る。そして、入力されたバイト列が、こちらが期待する文字コードでエンコードされているかどうかを判定する。本来は Encode::Guess を使うのだろうが、以前にも書いたようにこれはあまり当てにならないので、文字コードを指定してとにかくデコードを試みる。Encode::decode に3つ目の引き数 1 を与えることによって、デコードができなかった場合にエラーが発生する。


use CGI ':cgi';

use Encode 'decode';

use encoding 'EUC-JP', STDIN => undef;

$string = eval { decode('EUC-JP', param('name'), 1) };

if ( $@ ) {

# エラー処理

} else {

# 入力値チェック

}


UTF-8 を使う場合は、次のようにも書ける。utf8 プラグマは、ハードコードされた文字列をデコードするが、入出力は変更しない。そこで、binmode を使って標準出力だけをエンコードしてやる。


use CGI ':cgi';

use Encode 'decode';

use utf8;

binmode STDOUT, ':utf8';

$string = eval { decode('UTF-8', param('name'), 1) };

... 以下同じ

さんのコメント:

・HTMLタグは使えません。

・電子メールアドレスを含めないでください。

・コメントには全角文字を含めて下さい。

・長さの上限はおよそ800字です。

[この記事だけを読む。] [このカテゴリをまとめて読む。] [最新の記事を読む。]

羽鳥 公士郎