おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

文字コード(4)

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;
}