cgiと文字コード
binmode で出力をバイト文字列にする
出力先の文字コードが決まっていれば、いちいちスクリプトの中でエンコードするよりも、出力時点でまとめてエンコードした方が合理的です。ファイルをオープンするときに指定する方法もありますが、標準入出力のように、既にオープンしている出力先に対しては、binmodeを使って設定します。
binmode STDOUT, ":utf8";
このようにすると、print文などで出力された文字列は、出力段階でutf8のバイト文字列に変換されてから標準出力に出されます。いちいちスクリプトの中でエンコードする必要がなく、便利です。utf8以外のコードを指定することもできますが、ちょっと書き方が違って、例えばshift_jisの拡張であるcp932で出力したければ、
binmode STDOUT, ":encoding(cp932)";
のように書きます。
入力のどの段階で文字デコードするか
同様に、
binmode STDIN, ":utf8";
とすれば、入力データをUTF-8と解釈してデコードしてから、perl内部に読み込まれますが、cgiではそれではうまくいきません。というのは、データがURLエンコーディングされて送られてくるので、まずはそれをデコードしなければならないからです。
データ入力 -> URLエンコーディングのデコード -> 文字列のデコード
この順番でデコードしなければなりません。ですから、デコードについてはスクリプトの中で行うことになります。
サンプルcgi (文字コードを調べる)
今まで述べてきたことを元に、文字コードを調べるcgiをサンプルスクリプトとして作ってみました。参考にしてみてください。
#!/usr/bin/perl
#===========================================================
# 文字コード表示 0.1
# testCHARCODE.cgi
#
# 2012/8/16 ver 0.1
#===========================================================
#ユーザから見た操作の流れ
#
# 文字を入力して、送信ボタンを押す
#
#CGIから見た流れ
#
# POSTデータがあれば
# POSTデータを取り込み、
# デコード(URLエンジョーディング)、
# デコード(文字コード)、
# utf8, euc-jp, shiftjis のコードを計算
# htmlの出力
# ヘッダなどの出力
# 文字コードの計算結果があれば、表示
# FORMを使った入力枠などを表示
#
use utf8;
use Encode;
binmode STDOUT, ":utf8"; #出力はすべてutf8でエンコードしてバイト文字列にする
if ($ENV{'CONTENT_LENGTH'} > 0) {
read(STDIN, $postdata, $ENV{'CONTENT_LENGTH'});
@a = split(/&/, $postdata);
foreach $x (@a) {
($name, $value) = split(/=/, $x);
$value = decodeURL($value); # URLエンコーディングのデコード
$value = decode("utf8", $value); # 文字コードのデコード(POSTDATAはutf8という前提)
$FORM{$name} = $value;
}
@ccode = ("utf8", "euc-jp", "shiftjis");
$ch = $FORM{'JCHAR'}; #入力された日本語文字列
foreach $x (@ccode) {
$bch{$x} = encode($x, $ch); # 各々エンコードしたバイト文字列
@s = split(//,$bch{$x});
foreach $y (@s) {
$hexch{$x} .= sprintf("%02X ", unpack("C",$y) ); #そのバイト文字列のコードを文字列で表したもの
}
}
}
print "Content-type: text/html; charset=utf-8\n";
print "\n";
print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE>簡易文字コード表示</TITLE>\n";
print "</HEAD>\n";
print "<BODY>\n";
if ($ENV{'CONTENT_LENGTH'} > 0) {
print "<TABLE BORDER=1 CELLSPACING=0>\n";
print "<CAPTION>".encodehtml($ch)." の文字コード</CAPTION>\n";
foreach $x (@ccode) {
print "<TR ALIGN=LEFT><TH>$x</TH><TH>$hexch{$x}</TH></TR>\n";
}
print "</TABLE>\n";
print "<BR>\n";
}
print "<FORM action=\"testCHARCODE.cgi\" method=\"POST\" accept-charset=\"UTF-8\">\n";
print " 日本語の文字(列)を入力してください <INPUT type=\"text\" name=\"JCHAR\"><BR>\n";
print "<BR>\n";
print " <INPUT type=submit value=\"送信\">\n";
print "</FORM>\n";
print "\n";
print "</BODY>\n";
print "</HTML>\n";
exit();
#
#
#
# URLエンコーディングのデコード
sub decodeURL {
my $s = shift;
$s =~ tr/+/ /; #スペースの復元
$s =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; # %エンコーディングのデコード
return $s;
}
# htmlの特殊文字(&"<>)を無効化
sub encodehtml {
my $s = shift;
$s =~ s/&/&/g;
$s =~ s/"/"/g;
$s =~ s/</</g;
$s =~ s/>/>/g;
return $s;
}