二〇〇七年 師走 十九日 水曜日
Perl で Jcode を使わずに日本語を扱う
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) };
... 以下同じ
二〇〇五年 睦月 十九日 水曜日
Encode と Jcode
1月16日の記事アクセスログの referer を見る
で、文字コードを変換するのに Encode モジュールを使ったが、実際使ってみると、判定に失敗する場合が多い。Perl 5.8.x Unicode関連
によると、Jcode、Encode のメンテナの弾さん曰くGuessを使う場合の心得。1. 出来れば使わない 2. 以下略
ということなので、使わないことにした。
referer をデコードするサブルーチンを、Jcode モジュールを使って書き直すと、こうなる。
use Jcode;
my $referer = shift @_ or return 'n/a';
$referer =~ tr/+/ /;
$referer =~ s/%([a-f\d]{2})/pack 'H2', $1/egi;
jcode $referer;
最後の行は、Jcode->new($referer); と書いても同じ。オブジェクトの中では、文字列は EUC で格納されていて、オブジェクトが評価されると、その文字列が返る。
なお、Jcode モジュールは標準ではインストールされていないので、必要ならインストールすること。
コメントを書くには JavaScript が必要です。
二〇〇五年 睦月 十八日 火曜日
スパムプロクシ
再びアクセスログの referer を見ていると、referer がいかがわしいサイトの URL になっていることがある。不審に思って調べてみると、こういうのをリファラースパム(referral spam)というようだ。Wired News の記事によると、ブログの中には referer のログを公開しているところがあるので、そのログにサイトの URL を書き込むことをねらっているようだ。また、ログを見た人が、こんなサイトから私のブログにリンクが貼ってあるのか知らんと思って、サイトを見てみるということもあるだろう。(すみません、私も1つだけ見てしまいました。Windows を使っていたら、おそらくスパイウェアの1つや2つ入っていたことでしょう。)
私はログを公開していないので、今のところ実害はないが、あまり頻繁にくるようになれば、何か対策を立てなければならなくなる。で、詳しいデータをとってみると、案の定多段プロクシ経由でいらっしゃっているのだが、$ENV{'HTTP_VIA'} の中に 'pinappleproxy' というのが必ず含まれている。スパマーが必ずこれを使うとはいえないだろうが、これを経由してきたらスパマーだと断定してもよいのではないか。少なくとも、このような多段プロクシ経由でくる人は、ろくな人ではないだろう。
コメントスパムにお悩みの方にも参考になるかと思って書いてみた。
コメントを書くには JavaScript が必要です。
二〇〇五年 睦月 十六日 日曜日
アクセスログの referer を見る
アクセスログの referer には、ページを見に来てくれた人が、どのページから飛んできたかが記録されている。(ただし、ブラウザによっては、referer を返さなかったり、任意の referer を返すように設定できるので、信用はできない。)
それで、このブログのアクセスログを見ていたところ、Google から来る人が結構いらっしゃる。そのときは、referer の中に検索した文字列も含まれているのだが、日本語は URL エンコードされていて、そのままではなんのことだか分からない。そうなると、検索文字列を知りたくなるのが人情というもので、エンコードされた referer を受け取って、デコードしてから文字コードを EUC に変換するサブルーチンを作ってみた。
なお、Encode モジュールは perl 5.8 から標準装備になった。
use Encode;
use Encode::Guess qw(euc-jp shiftjis 7bit-jis);
my $raw_referer = shift @_ or return 'n/a';
my $referer = $raw_referer;
$referer =~ tr/+/ /;
$referer =~ s/%([a-f\d]{2})/pack 'H2', $1/egi;
my $decorder = guess_encoding $referer;
return $raw_referer unless ref($decorder);
encode 'euc-jp', $decorder->decode($referer);
「初等幾何」と「篠崎史子」で最初のページにでるのか。なかなか不思議な組み合わせだ。
コメントを書くには JavaScript が必要です。
二〇〇五年 睦月 十日 月曜日
split 関数についてのメモ
split 関数は、最初の引数に /PATTERN/ という形をとる。では、「/」で分割したいときは /\// と書く必要があるかと思いきや、"/" でも大丈夫。つまり、以下の3つは同じ結果になる。
split /\// ; split '/' ; split "/" ;
どうやら、たいていの場合に、/PATTERN/ の代わりに 'PATTERN' や "PATTERN" を使っても、同じ結果が返るようだ。正規表現も使えるし、以下の3つも同じ。
split // ; split '' ; split "" ;
ただし、"PATTERN" の中の逆スラッシュは、文字列リテラルと同じように扱われるので、たとえば「.」で分割したいときは、以下のようにする。
split /\./ ; split '\.' ; split "\\." ;
コメントを書くには JavaScript が必要です。

コメントを書くには JavaScript が必要です。