use CGIでcharsetの指定をする

| | トラックバック(1)

やっとわかった・・・。

use CGI することで使うことができるCGIモジュールは
CGIをPerlでプログラムする際に非常に便利だが、
デフォルトで使うと文字が英語で扱われるので文字化けする。
CGIの動作自体は大丈夫だけど、こと文字に関しては非常にややこしい。

まず、どデフォルトでやるとこうで、

use CGI;

my $cgi = CGI->new;

print $cgi->header(), $cgi->start_html();
print "<p>ほげほげ</p>";
print $cgi->end_html();

こうすると生成されるhtmlはまちがいなく文字化けする(ほげほげの所)。

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<p><ここが文字化け></p>
</body>
</html>

解説すると、「my $cgi = CGI->new」これでCGIオブジェクトのインスタンスを生成し、
$cgiという変数に格納している。

そして「$cgi->header()」で「Content-Type: text/html」の部分が生成されている。

CGIのプログラムをはじめて一番最初につまずくのがたぶんここで、
「まずは<html>タグを生成して、それからbodyタグ〜」という風に、
HTMLタグをいきなり送り出そうとする。

だけど、それだと必ずInternal Server Errorかなんかになる。
サーバーは最初に、ブラウザに対して
「これからお送りしますのは、テキストで書かれたHTMLですよ〜」というメッセージを
送信しなければならない。
そしてこれはhtmlファイルにはどこにも出てこない。

CGIプログラム初心者のころはこれでよくはまった。
コンソールで実行する分にはプログラムは動作するのに、
サーバーによって実行させようとした途端動かなくなったら、
このあたりに原因があると思ってよい。

もしCGIモジュールを使わない場合、
サーバーからブラウザへの最初の一行は
「Content-Type: text/html;(+改行2回?)」にして、
それからHTMLを書き始めればOKである。
CGIモジュールを使うと
この部分を「$cgi->header()」が自動的に生成してくれる。
(けっこうContentとTypeの間ってハイフンだっけスペースだっけ?とか
 Content-Typeのあとコロンいるのか?とかおぼつかなくなる)

「$cgi->start_html」はその名の通りHTMLの最初の部分を自動的に
吐き出してくれる。
デフォルトだと、以下のような文字列を吐き出す。

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>

DOCTYPE宣言、HTMLのスタートタグ、headタグ、bodyのスタートタグ。

そしてこれと同様に「$cgi->end_html」は最後の

</body>
</html>

を生成してくれるというわけである。
あとはこちらがBODY部分を書けばよいわけだが、
HTMLのヘッダ部分はよくみるとよろしくないことが書いてある。

例えばHTMLの開始タグ。

<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">

「lang="en-US"」って・・・。
「xml:lang="en-US"」って・・・。

さらにメタタグ。

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

何そのcharset? charset=iso-8859-1?

完全にアメリカナイズされたHTMLを吐きやがるのである。
これでは「ほげほげ」が文字化けするのも当然である。

で、この文字化け問題は、文字コードを指定してあげると一応解決する。
単に、$cgi->headerにcharsetの引数を渡してあげればよい。
もし、そのPerlスクリプトが「utf-8」で書かれているのなら

$cgi->header( -charset => "utf-8")

というように該当部分を書き換えると、
日本語は表示されるようになる。
(ブラウザにもよるとは思う)

しかし、metaタグのhttp-equivのところは、「utf-8」ではなく、
さっきの「iso-8859-1」のままなのである。

metagタグ内のcharset設定が間違っていても文字化けしなかったのは
「$cgi->header( -charset => 'utf-8' )」とすることで
「Content-Type: text/html; charset=utf-8;」とcharsetも指定して
サーバからブラウザに送っているためだと思われる。

さらに、html開始タグのlangもenのまま。

これでは、ブラウザがそちらを優先した場合、表示が崩れる可能性がある。

で、ここからがやっと本題。
このことがずっと気持ち悪くて修正する方法を探していた。
$cgi->headや$cgi->start_htmlなどに適切な引数を渡してあげれば
よいのだろうと思っていたのだが、
そのうまい渡し方がわからなかったのだ。

最初はこうやっていた。

my $meta = $cgi->meta( {    -http_equiv => 'Content-Type',    -content => 'text/html; charset=UTF-8' } );

print $cgi->header( -charset => "utf-8", );
print $cgi->start_html(
   -lang => 'ja',
   -head => $meta,
);

これだと、確かにmetaタグは正しいものが生成されるのだが、
しかし古いmetaタグも残ってしまう。

生成されるタグは以下のよう。(DOCTYPE宣言はカットした)

<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<title>log viewer</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>

meta http-equiv... というタグが2種類生成されてしまう。
html開始タグのlang指定はうまくいっている。

で、わかってしまえばなんでこのことに早く気づかなかったのだろう
という気がするのだが、以下のようにすればこの問題は解決する。

print $cgi->header( -charset => "utf-8", );
print $cgi->start_html(
   -lang => 'ja',
   -encoding => 'utf-8'
);

あっけないけれど、これでOKである。

生成されるHTMLは以下。

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<title>title</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<p>ほげほげ</p>
</body>
</html>

というわけでめでたしめでたし。(本当にこれが'正しい'やりかたなのかは不明)

意味がわからないのは、
「$cgi->header( -charset => 'utf-8' )」とやったときに、
$cgiはcharsetがutf-8だということは知っているはずなのに、
それがstart_htmlの時には反映されていないということである。

これはバグなのか、それともタグ出力の柔軟性を保つために
わざとやっているのかは不明。

comments powered by Disqus

トラックバック(1)

このブログ記事を参照しているブログ一覧: use CGIでcharsetの指定をする

このブログ記事に対するトラックバックURL: http://nozawashinichi.sakura.ne.jp/MT-4.25/mt-tb.cgi/619

CGIを使ったメールを送信するプログラムを作りたいなと思っていた。 だけど、メー... 続きを読む

comments powered by Disqus

このブログ記事について

このページは、Shinichi Nozawaが2009年9月28日 16:40に書いたブログ記事です。

ひとつ前のブログ記事は「DD_belatedPNGを導入する」です。

次のブログ記事は「CGI.pmのメソッド」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。