CGI.pm の textfield(), popup_menu(), checkbox_group() などを使ってフォームの部品の HTML を出力させたとき、テキストフィールドのデフォルト文字列やセレクトボックスのラベル部分などが一部だけ文字化けしてしまうことがある。
これは、 CGI::escapeHTML() のエスケープ処理が原因 (textfield() などは内部で escapeHTML() を呼んでいるため、影響を受ける)。
いろいろなパターンを検証したわけではないが、
- ソースコードの文字エンコーディングは UTF-8
- 出力する文字エンコーディングも UTF-8
のときに、文字化けがおこった。
これを回避するためには、
- CGI::autoEscape(0) とする
- CGI::charset("utf-8") とする
いずれかをやってから textfield() などを呼ぶと、文字化けしなくなる。以下検証用コード。
#!perl use strict; use CGI; use Test::More; plan tests => 9; diag("普通に使う"); { # textfield のデフォルト値 isnt( CGI::textfield( -name => "job", -value => "ニート" ), q{<input type="text" name="job" value="ニート" />}, "textfield 文字化け", ); # popup_menu (selectbox) のラベル isnt( CGI::popup_menu( -name => "job", -values => [ qw(1 2) ], -labels => { 1 => "無職", 2 => "ニート" } ), q{<select name="job" > <option value="1">無職</option> <option value="2">ニート</option> </select>}, "popup_menu 文字化け", ); # checkbox_group のラベル isnt( CGI::checkbox_group( -name => "job", -values => [qw(1)], -labels => { 1 => "ニート" }), q{<label><input type="checkbox" name="job" value="1" />ニート</label>}, "checkbox_group 文字化け", ); } diag(q{CGI::charset("utf-8") をセットする"}); { CGI::charset("utf-8"); # textfield のデフォルト値 is( CGI::textfield( -name => "job", -value => "ニート" ), q{<input type="text" name="job" value="ニート" />}, "textfield 化けない", ); # popup_menu (selectbox) のラベル is( CGI::popup_menu( -name => "job", -values => [ qw(1 2) ], -labels => { 1 => "無職", 2 => "ニート" } ), q{<select name="job" > <option value="1">無職</option> <option value="2">ニート</option> </select>}, "popup_menu 化けない", ); # checkbox_group のラベル is( CGI::checkbox_group( -name => "job", -values => [qw(1)], -labels => { 1 => "ニート" }), q{<label><input type="checkbox" name="job" value="1" />ニート</label>}, "checkbox_group 化けない", ); } diag("CGI::autoEscape(0) をセットする"); # これは $cgi->autoEscape(0) としてから $cgi->textfield() などを呼んでも良い { CGI::autoEscape(0); is( CGI::textfield( -name => "job", -value => "ニート" ), q{<input type="text" name="job" value="ニート" />}, "textfield 化けない", ); # popup_menu (selectbox) のラベル is( CGI::popup_menu( -name => "job", -values => [ qw(1 2) ], -labels => { 1 => "無職", 2 => "ニート" } ), q{<select name="job" > <option value="1">無職</option> <option value="2">ニート</option> </select>}, "popup_menu 化けない", ); # checkbox_group のラベル is( CGI::checkbox_group( -name => "job", -values => [qw(1)], -labels => { 1 => "ニート" }), q{<label><input type="checkbox" name="job" value="1" />ニート</label>}, "checkbox_group 化けない", ); }
escapeHTML() を読むと、文字コードが "ISO-8859-1" か "WINDOWS-1252" だった場合に、 "\x8b" と "\x9b" という二文字をエスケープしてそれぞれ "‹" と "›" にしている。コメントによると、 "# Handle bug in some browsers with Latin charsets" だそうで、ブラウザのバグに対処するためのコードらしい。
この "\x8b" "\x9b" という二文字は、 Terminal.app で文字エンコーディングを「欧米 (Windows Latin 1)」にしたときに以下のようにすると表示される文字(UTF-8 だと ? になってしまう)。 wordpress で作ってあるブログのタイトル部分によく使われている、 < と > の小さいっぽい文字のようだ。
kyanny$ perl -e 'print q{\x8b}, " => ", "\x8b", "\n"' \x8b => ‹ kyanny$ perl -e 'print q{\x9b}, " => ", "\x9b", "\n"' \x9b => ›