作成日:2004年06月06日、更新日:2004年07月09日 作成:鷹の巣

サイト全体のWebページの文字コードをUTF-8に統一した時期の覚書です。


Webページの文字コードをUTF-8にする方法(後編)

後編では、動的Webページ(CGI)の文字コードをUTF-8にする方法を示します。CGIは、perl5.8のスクリプトについて説明します。前編で静的なWebページが、全てUTF-8コードになりましたので、perlのスクリプト自身も全てUTF-8コードにします。前編で述べました様にperlのスクリプトを保存する場合は、BOM(Byte Order Mark)なしのUTF-8Nコードで保存しなければなりません。

目次

  1. [前編]まえがき
  2. [前編]Webページの文字コードをUTF-8にする方法
  3. [前編]Webページの検索文字コードをUTF-8からeuc-jp(Shift_JIS)にする方法
  4. 動的Webページ(CGI)の文字コードをUTF-8にする方法
    1. 文字コード変換の基本と注意する点
    2. CGIの主たる改造例その1(UTF8フラグを付けない場合で推奨)(2004.06.20誤記訂正)
    3. CGIの主たる改造例その2(UTF8フラグを付ける場合)(2004.06.20追記)
    4. UTF-8とは全く関係ないおまけの改造例(Kent WebさんのFantasy Boardの改造例)(2004.07.09追記)
  5. Webサーバー(AN HTTPD)にUTF-8の文字コードを設定する方法
  6. あとがき

4. 動的Webページ(CGI)の文字コードをUTF-8にする方法

まず、perlのスクリプト自身をUTF-8コードで記述し、文字コードを全てUTF-8コードにするには、Perl 5.8.x Unicode関連に書かれている様な内容を理解する必要があります。以下に文字コード変換の基本と注意する点をまとめ、CGIの主たる改造例を示します。

4.1 文字コード変換の基本と注意する点

  1. 既知の文字コードから、UTF-8コードへの変換

    use Encode qw/ decode /;
    $utf8 = decode ( 'shiftjis' , $shiftjis );

    Shift_JISコードの$shiftjisをUTF-8コードに変換して$utf8に代入。$utf8には、UTF8フラグが付く

  2. UTF-8コードから、既知の文字コードへの変換

    use Encode qw/ encode /;
    $shiftjis = encode ( 'shiftjis' , $utf8 );

    UTF-8コードでUTF8フラグが付いた$utf8をShift_JISコードに変換して$shiftjisに代入。
    $shiftjisには、当然ながら、UTF8フラグが付かない
    $utf8 = encode ( 'utf8' , $utf8 );
    とすると、UTF-8コードのままであるが、UTF8フラグは付かない(消される)

  3. UTF8フラグを付けずに既知の文字コードの$stringから、既知の文字コードの$stringへの変換

    use Encode qw/ from_to /;
    from_to ( $string , 'shiftjis' , 'euc-jp' );

    from_to ( $string , 'shiftjis' , 'utf8' );とすると、UTF-8コードに変換されるが、UTF8フラグは付かない

  4. 未知の文字コードから、UTF-8コードへの変換

    use Encode::Guess qw/ shiftjis euc-jp 7bit-jis /;
    use Encode qw/ decode /;
    $enc = guess_encoding ( $string );
    if ( ref $enc ) { $utf8 = decode ( $enc->name , $string ); }

    Guessには、asciiとutf8は初期値として設定済みの為、省略可能。以下の様に書いても結果は同じ。
    use Encode::Guess qw/ shiftjis euc-jp 7bit-jis ascii utf8 /;

  5. perlのスクリプトをUTF-8コードで記述しただけでは、スクリプトに書いてある変数には、UTF8フラグは付かない

    use Encode qw/ encode /;
    $string1 = '自宅サーバー';
    $string2 = encode ( 'shiftjis' , $string1 );

    としても、$string1にUTF8フラグが付いていないので、$string2はShift_JISコードにならない。ただし、「use utf8;」を使用して、perlのスクリプト自身がUTF-8コードで記述していると宣言しておけば、$string2に代入する時に自動的(やみくも)にUTF8フラグを付けられて、Shift_JISコードになる。しかし、ISO 8859-1 (Latin-1) Character Setで不具合を生じる。

    use Encode qw/ encode /;
    $string1 = '自宅サーバー';
    utf8::decode ( $string1 ); # または、$string1 = decode ( 'utf8' , $string1 );
    $string2 = encode ( 'shiftjis' , $string1 );

    としてUTF8フラグを付けた後にencodeすれば、$string2はShift_JISコードになる。

  6. 上記の理由から、「use utf8;」は一般的なCGIとしては、使用できない

  7. しかし、「use utf8;」の代わりに「use encoding 'utf8';」と書くと、スクリプトの最初の実行(コンパイル)時にperlのスクリプト内の全ての文字がUTF-8コードに変換(decode)され、UTF8フラグが付くので有効である。ただし、応答が悪くなるので、CGIでは使用しない方が良い。従って、必要に応じてutf8::decode ( $string1 );で、UTF8フラグを付ける方が良い。

4.2 CGIの主たる改造例その1(UTF8フラグを付けない場合で推奨)

Kent WebさんのFantasy Boardを例に以下に改造内容を示します。このperlスクリプトは、Shift_JISコードで記述されています。尚、use utf8;やuse encoding 'utf8';は、記述しません。

  1. fantasy.cgiとfantasylog.cgiをテキストの編集ツールを使用してUTF-8Nで保存する。下記の様にファイルの入出力をUTF-8コードに指定しておいた方が良い。 尚、「use open ":utf8";」をコメント文にするとファイルより読んできた内容にUTF-8フラグが付かなくなるので、管理者画面での文字化けは、なくなります。気になる方は、コメント文にして下さい。

    #!/usr/local/bin/perl
    
    # use utf8; <--- UTF8フラグが付かない様にするため、未使用。
    use open ":utf8"; # <--- fantasylog.cgiをUTF-8コードで入出力。管理者画面で一部文字化けするが、運用上問題なし。
    # binmode(STDOUT,":utf8"); <--- UTF8フラグが付かない様にするため、未使用。
    
    #┌─────────────────────────────────
    #│ Fantasy Board v2.1 (2004/01/29)
  2. fantasy.cgiのスクリプトで、htmlファイル出力のメタタグ部分のShift_JISコードをUTF-8に変更する。(1ヶ所)

    # <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">
    <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
  3. fantasy.cgiのスクリプトで、jcode.plを使用している行を変更する。(1ヶ所)

    # コード変換ライブラリ取込
    # require './jcode.pl';
    use Encode::Guess qw/ shiftjis euc-jp 7bit-jis /;
    use Encode qw/ from_to /;
  4. fantasy.cgiのスクリプトで、&jcode'convertを使用している行を変更する。(2ヶ所)

                    # S-JISコード変換
    #               &jcode'convert(*val, 'sjis'); <--- Shift_JISコードに変換
                    # UTF-8コード変換
                    my $enc = guess_encoding ( $val );
                    if ( ref $enc ) {  from_to ( $val , $enc->name , 'utf8' ); } # <--- UTF-8からJISコードにUTF8フラグを付けずに変換
    
    ・・・
    
    #               &jcode'convert(*_, 'jis', 'sjis'); <--- Shift_JISからJISコードに変換
                    from_to ( $_ , 'utf8' , '7bit-jis' ); # <--- UTF-8からJISコードにUTF8フラグを付けずに変換

4.3 CGIの主たる改造例その2(UTF8フラグを付ける場合)

Kent WebさんのFantasy Boardを例に以下に改造内容を示します。このperlスクリプトは、Shift_JISコードで記述されています。以下の方法では、クッキーの一部に全角文字の「@」等が含まれている場合は、クッキーを正確に復元できません。

動作確認環境:

  1. fantasy.cgiとfantasylog.cgiをテキストの編集ツールを使用してUTF-8Nで保存する。下記の様にファイルの入出力をUTF-8コードに指定しておいた方が良い。

    #!/usr/local/bin/perl
    
    use utf8; # <--- スクリプトがUTF-8コードで書かれていることを宣言する。
    use open ":utf8"; # <--- 書いていなくても良いが、fantasylog.cgiがUTF-8コードであることを指定しておく。
    
    #┌─────────────────────────────────
    #│ Fantasy Board v2.1 (2004/01/29)
  2. fantasy.cgiのスクリプトで、htmlファイル出力のメタタグ部分のShift_JISコードをUTF-8に変更する。(1ヶ所)

    # <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">
    <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
  3. サブルーチン名のdecodeをCGI_decodeに変更する。(2ヶ所)

    # &decode;
    &CGI_decode;
    
    ・・・
    
    #-------------------------------------------------
    #  デコード処理
    #-------------------------------------------------
    #  sub decode {
    sub CGI_decode {

    ※このサブルーチン名を変更しないと、UTF-8コードにデコード(decode)出来なくなります

  4. fantasy.cgiのスクリプトで、jcode.plを使用している行を変更する。(1ヶ所)

    # コード変換ライブラリ取込
    # require './jcode.pl';
    use Encode::Guess qw/ shiftjis euc-jp 7bit-jis /;
    use Encode qw/ decode from_to /;
  5. fantasy.cgiのスクリプトで、&jcode'convertを使用している行を変更する。(2ヶ所)

    # S-JISコード変換
    #             &jcode'convert(*val, 'sjis'); <--- Shift_JISコードに変換
    # UTF-8コード変換
    my $enc = guess_encoding ( $val );
    if ( ref $enc ) { $val = decode ( $enc->name, $val ); } else { $val = decode ( 'utf8' , $val ); } # <--- UTF-8コードに変換
    
    ・・・
    
    #             &jcode'convert(*_, 'jis', 'sjis'); <--- Shift_JISからJISコードに変換
    from_to ( $_ , 'utf8' , '7bit-jis' ); # <--- UTF-8からJISコードにUTF8フラグを付けずに変換
  6. fantasy.cgiのスクリプトで、クッキー取得回路にdecodeを追加する。(1ヶ所)

            # データをURLデコードして復元
            @cook=();
            foreach ( split(/<>/, $cook{'FANTA_BBS'}) ) {
                    s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("H2", $1)/eg;
                    my $enc = guess_encoding ( $_ );
                    if ( ref $enc ) { $_ = decode ( $enc->name, $_ ); } else { $_ = decode ( 'utf8' , $_ ); }
    
                    push(@cook,$_);
  7. fantasy.cgiのスクリプトで、漢字以外の英数字の入る変数の後に\記号を追加する。(3ヶ所)

            if ($back >= 0) {
                    print "<td><form action=\"$script\" method=\"POST\">\n";
                    print "<input type=hidden name=page value=\"$back\">\n";
                    print "<input type=submit value=\"前の$p_log\件\"></form></td>\n";
            }
            if ($next < $i) {
                    print "<td><form action=\"$script\" method=\"POST\">\n";
                    print "<input type=hidden name=page value=\"$next\">\n";
                    print "<input type=submit value=\"次の$p_log\件\"></form></td>\n";
            }
    
    ・・・
    
    <LI>記事の保持件数は<b>最大 $max\件</b>です。それを超えると古い順に自動削除されます。<P>

4.4 UTF-8とは全く関係ないおまけの改造例(Kent WebさんのFantasy Boardの改造例)(2004.07.09追記)

UTF-8とは全く関係ないのですが、メールアドレスを収集するクローラに対する防御対策として、Kent WebさんのFantasy Boardの改造内容を示します。詳細は、メールアドレスの保護(メールアドレス収集ソフトに負担を強いる)をご参照願います。

注意)
自宅サーバー以外のプロバイダ等に設置する場合は、まずスクリプトに「use HTML::Entities;」を一行書き加えて下さい。もし、これでCGI Errorが出たら、HTML::Entitiesモジュールが使用できません。この場合は、メールアドレス収集ソフトに負担を強いるCGI用perlスクリプト例その1(HTML::Entitiesモジュール未使用)をご参考にサブルーチン(email_link)を改造して下さい。 下記のスクリプトの例として、メールアドレス収集ソフトに負担を強いるCGI用perlスクリプト例その2(HTML::Entitiesモジュール使用)もあります。

  1. fantasy.cgiのスクリプトの先頭付近に追加しておく。(1ヶ所)

    use HTML::Entities;
    
    # メールアドレスをHTMLエンティティ出力形式に変換する。
    # 参考URL http://ab.jpn.ph/soft/html_rand.html
    # 1=変換する、0=変換しない
    $mail_entity = 1;
  2. fantasy.cgiのスクリプトの末尾にサブルーチンを追加する。(1ヶ所)

    #----------------------------------------------------#
    # メールアドレスのリンクをHTMLエンティティ表示にする #
    #----------------------------------------------------#
    sub email_link {
            my ( $e_mail , $e_name ) = @_;
            my $mailto;
            if ($mail_entity) {
                    encode_entities($e_mail ,"\x00-\xff");
                    $mailto = "&#109;&#97;&#105;&#108;&#116;&#111;&#58;$e_mail";
            } else {
                    $mailto = "mailto:$e_mail";
            }
            return "<a href=\"$mailto\">$e_name</a>";
    }
  3. fantasy.cgiのスクリプトで、メールアドレスのリンク部分を変更する。(3ヶ所)

    #               if ($mail) { $name = "<a href=\"mailto:$mail\">$name</a>"; } <--- 2ヶ所
                    if ($mail) { $name = &email_link ( $mail , $name ); }
    
    #               if ($eml) { $nam = "<a href=\"mailto:$eml\">$nam</a>"; } <--- 1ヶ所
                    if ($eml) { $nam = &email_link ( $eml , $nam ); }

5. Webサーバー(AN HTTPD)にUTF-8の文字コードを設定する方法

Webサーバー(AN HTTPD)のhttp応答ヘッダにUTF-8の文字コードを設定する方法を示します。

前編の冒頭で述べましたが、このサイトで使用しているWebサーバーのAN HTTPDは、http応答ヘッダに個別のWebページの文字コードを付加出来ません。注)下図の様に設定しますと全てのhtmlファイルのhttp応答ヘッダは、Content-Type: text/html;charset=utf-8となって文字コードのutf-8が付加されます。

注)他のWebサーバーを使用するとhttp応答ヘッダに個別のWebページの文字コードを付加出来るということではありません。

下図の様に「オプション/一般」の拡張子に対するMIMEタイプを3ヶ所変更すれば、http応答ヘッダにUTF-8の文字コードが設定されます。

特に拡張子がtxtとtextの場合、サイトのtextファイルの文字コードを統一して、http応答ヘッダに文字コードを付加することを推奨します。これは、ブラウザでの文字化け対策に大変、有効だと考えます。

AN HTTPDのMIMEタイプの設定例

上記の様に設定して、Server Header Checkerのサイトからアクセスすると、Content-Typeがtext/html;charset=utf-8になっていることが確認出来ます。

6. あとがき

当サイトのWebページをUTF-8コードにすると検索窓が使用できなくなったり、CGIのコード変換のdecodeがうまく出来なかったりしました。色々と障害が発生して、私自身が解決した内容しか記載していませんが、何かのお役に立てると嬉しいです。

CGIで利用しているPerlには、UTF-8コードが標準で実装されています。しかし、Perlのスクリプトは、普通、UTF-8Nコードで記述されないので、UTF8フラグを意識してコーディングしなければなりません。このあたりが大変難しくなっています。

簡単な説明でしたが、専門用語を極力、排除して留意すべき実務的な内容の説明を行ったつもりです。わかりにくい点がございましたら、掲示板にご投稿をお願い致します。より理解しやすいWebページに改善して行きたいと考えています。

目次▲頁先頭