Corredor

ウェブ、プログラミングの勉強メモ。

Perl CGI でリクエスト文字列をファイル書き出ししたら文字化けしたのを直した

POST 送信されたリクエスト文字列を取得し、それをファイルに書き出すという Perl CGI を書いていた。よくある掲示板システムみたいなモノだ。

昔の Shift-JIS を使っていた頃とは違って、今は表示する Web ページも UTF-8、Perl スクリプトファイルも UTF-8、AJAX 通信するので当然 UTF-8、全部 UTF-8 なら文字化けなんてないだろ…と思っていたら、ファイルに書き出した文字列が盛大に化けていた。

そこでよくよく調べてみると、何やら Perl では utf8::decode() という関数を使う必要があることが分かった。

Perl における文字コード設定色々

Perl CGI における文字コード設定はいくつかの場所で出てくる。それぞれの効能を理解しておかないと、今回の文字化けは解消できなさそうだったので、色々調べてみた。

まずスクリプトの冒頭で、ファイル全体に対して適用したいものとして記述したのは、最終的に以下の3行になった。

use utf8;
use open ':encoding(UTF-8)';
binmode(STDOUT, ':encoding(UTF-8)');
  • use utf8;
    • この CGI ファイルのエンコーディングを示す。
    • コレにより、スクリプトが扱う全角文字データに UTF-8 フラグが付いているよ、という宣言になるようだ。
    • 後で分かるが、今回の文字化けはこの UTF-8 フラグが付いていない全角文字を処理したことが原因だった。
  • use open ':encoding(UTF-8)';
    • ファイル入出力で使用する open() 関数のエンコーディングを示す。
    • open() の第2引数で示すのと同じ内容。
    • :utf8 と書くとエンコードチェックしないので、省略せず :encoding(UTF-8) と書くのが良いようだ。
  • binmode(STDOUT, ':encoding(UTF-8)');
    • 標準出力 STDOUT 向けのエンコーディングを示す。
    • コレがないと Wide character in print といったワーニングが出力される。

utf8::decode() 関数

今回、リクエスト文字列を受け取って、それをファイルに書き込んでいたワケだが、リクエスト文字列は外部から拾ってきた文字列なので、Perl のスクリプト内部で使用している文字列と違って UTf-8 フラグというモノが付いていなかった。

# POST 送信されたリクエスト文字列を受け取る
my $queryString;
read(STDIN, $queryString, $ENV{'CONTENT_LENGTH'});
# ↑ この $queryString には UTF-8 フラグが付いていない

# ただの文字列をスクリプト内で宣言する
my $exampleStr = 'テスト';
# ↑ この $exampleStr は Perl スクリプト内で用意しているので自動的に UTF-8 フラグが付いている

変数 $queryStringkey=value な文字列になっているので、分解して連想配列 (ハッシュ) にする関数を組んでいた。そこで、分解した value に対して UTF-8 フラグを付けるため、utf8::decode() 関数を適用するようにした。

# クエリ文字列を連想配列に変換する (GET・POST 対応)
sub parseQueries {
  # リクエスト情報をまとめる連想配列
  my %formData;
  
  # QueryString を取得する
  my $queryString;
  if($ENV{'REQUEST_METHOD'} eq 'POST') {
    read(STDIN, $queryString, $ENV{'CONTENT_LENGTH'});
  }
  else {
    $queryString = $ENV{'QUERY_STRING'};
  }
  
  # 'name=value' の配列に直す
  my @queryPairs = split('&', $queryString);
  foreach my $queryPair (@queryPairs) {
    my ($name, $value) = split('=', $queryPair);
    # パーセントエンコーディング文字列を戻す
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
    
    # UTF-8 フラグを付ける!
    utf8::decode($value);
    
    $formData{$name} = $value;
  }
  
  return %formData;
}

%formData のキー文字列に使用する $name の方は、ファイルへの書き出しをしなかったので特にデコードしなかった。試してはいないが、split() する前に $queryString ごとデコードしてしまっても良いのかも。

とりあえずコレでファイル書き出し時の文字化けは解消できた。

参考文献

Perl CPANモジュールガイド

Perl CPANモジュールガイド

  • 作者: 冨田尚樹,タナカユカリ
  • 出版社/メーカー: ワークスコーポレーション
  • 発売日: 2011/04/08
  • メディア: 単行本(ソフトカバー)
  • 購入: 20人 クリック: 2,028回
  • この商品を含むブログ (21件) を見る