PHPの文字化けについて

2005/09/30変更

PHPを使っていると設定の不備、コーディングの問題などで日本語が文字化けすることがあります。これで困っている人が多いようですので別途にまとめました。

以下の説明は私が過去に経験したことをまとめましたが、すべての条件について書いているわけではありません。もしも、これら以外で文字化けしている人がおられましたら、是非、掲示板などでお知らせください。


php.iniの設定ミス

まず、確認のため、php.iniで文字化けに絡む部分をピックアップしておきます。もう一度、設定値を確認してください。ここではPHP4.3.xについて書いてます。

php.ini 設定表
OS
スクリプトを保存する文字コード
(mbstring.internal_encoding)
ブラウザへ出力する文字コード
(mbstring.http_output)
php.iniの設定
Windows UTF-8 シフトJIS php.ini
EUC php.ini
UTF-8 php.ini
EUC シフトJIS php.ini
EUC php.ini
UTF-8 php.ini
Linux UTF-8 シフトJIS php.ini
EUC php.ini
UTF-8 php.ini
EUC シフトJIS php.ini
EUC php.ini
UTF-8 php.ini

設定内容の確認のため、以下のスクリプトを保存してブラウザで表示してみてください。「mbstring」セクションの「Master Value」の値がphp.iniの設定値と同じになっているか確認してください。私はコメントを外すのを忘れていて、ちゃんと設定されてないことがありました。

<?php phpinfo() ?>

ここまで大丈夫なら、次のスクリプトを実行してみます。テストする文字列は「ソリューション表示」がいいかと思います。この文字列は文字化けしやすいです。
このスクリプトのシフトJIS用EUC用をダウンロードできるようにしておきました。ダウンロード後、解凍して動作確認してください。

このスクリプトの実行結果が「入力した文字列は『ソリューション表示』ですよね?」と表示されるようなら、php.iniの設定は間違っていません。

準備したスクリプト
<html>
<body>
<form method="post" action="<?php echo $_SERVER["REQUEST_URI"] ?>">
<p>テスト文字列
<input type=text name=iTest size=60 value="ソリューション表示">
<input type=submit value=" テスト ">
</p>

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST')
  echo "<hr><p>入力した文字列は「" . htmlspecialchars($_POST['iTest']) . "」ですよね?</p>";
?>
</form>
</body>
</html>


php.iniの設定値と、実際のスクリプトとの食い違い

php.iniの「mbstring.internal_encoding = 」の設定と、実際に保存してするスクリプトの文字コードの不一致によって文字化けします。

「mbstring.internal_encoding = EUC-JP」にしているなら、スクリプト・ファイルはEUCで保存してください。

「mbstring.internal_encoding = SJIS」にしているなら、スクリプト・ファイルはシフトJISで保存してください。

Windowsでスクリプトを作っていると間違えやすいです。


httpd.confの問題

掲示板で教えていただいた情報では、Redhat9にPHPをインストールすると文字化けすることがあるようです。

回避方法は2つあります。

  1. /etc/httpd/conf/httpd.confに次の行を追加します。(初期値ではコメントアウトされているそうです)
    DefaultLanguage ja
    これで直ったとご報告をもらいました。
  2. HTTPヘッダーへ直接、出力する方法です。たとえば、各スクリプトの先頭で以下のようにします。
    @header('Content-Type: text/html; charset=Shift_JIS');
    @header('Content-Language: ja');

なお、掲示板情報から以下も追加設定するとFedoraなどでの文字化けが解消するそうです(これは私は未確認です)。
AddDefaultCharset off
AddCharset EUC-JP .euc-jp
AddCharset shift_jis .sjis
AddCharset ISO-2022-JP .jis


日本語を含む文字列に文字列関数を使う場合

うちの掲示板で、指摘がありましたので、ここに書いておきます。

日本語を含む文字列が代入された変数に、文字列関数を使う場合、先頭にmb_が付いた関数を使うようにしましょう。 普通の文字列関数を利用するとバイト単位で動作するので、文字化けする可能性があります。

あえてバイト別に処理したいようなときは、mb_()関数ではうまくいきませんので、普通の文字列操作関数を使うことになると思います。


文字列変数を出力するときの注意

echoなどで文字列を出力するとき、'<', '>', '&' などが文字列に含まれている時、ブラウザがHTMLタグと勘違いするので文字化けしたり、ページ自体がおかしな表示になります。

文字化けするサンプル
<?php
$str = "この文字列'<\">は文字化けするよ";
echo "<input type=text value='" . $str . "'>";
?>
文字化けを回避するサンプル
<?php
$str = "この文字列'<\">は文字化けするよ";
echo "<input type=text value='" . htmlspecialchars($str) . "'>";
?>

このようにして htmlspecialchars を使って表示するようにします。特にフォームやデータベースから取得するデータでは、このような文字化けが頻繁におきますので注意が必要です。


PHPスクリプト内部で出力文字コードを指定したい場合

文字化けとは直接関係ありませんが、掲示板で「特定のページのみ出力文字コードを変えたい」というお話がありました。.htaccessで変えてもいいのですが、スクリプトによって動的に出力文字コードを変えたいこともあるかと思いますし、この関係で文字化けする場合もあるようですからここにメモを書いておきます。

PHP4よりob_start()関数が追加されました。これはphp.iniの設定値output_bufferingのかわりになるような感じなんです。 このことから、ob_start()を使うとoutput_bufferingとoutput_handlerを動的に変えることができるようです。

以下の例では、internal_encodingがEUC-JPで、php.iniや.htaccessではSHISかEUC-JPに設定してあるにもかかわらず、UTF-8でブラウザ出力するようにスクリプトを作った例です。

<?php
define('Charset', 'UTF-8');	// 出力する文字コードの定義

// output_bufferingが定義されていたら、すべてOffにする
// これをしておかないと、すでに設定済みのob_startも効いてしまいます。
while (@ob_end_clean());

// このページの出力文字コードを指定し、output_buffering,mb_output_handlerを再設定する
// ob_startを実行する前にmb_http_outputで文字コードを設定しておくと、
// その文字コードへの変換が有効となる。
mb_http_output(Charset);
ini_set("default_charset", Charset);
ob_start('mb_output_handler');

// この後にブラウザへ出力すると、すべてUTF-8での出力となる
?>

ブラウザの問題

たとえば、ブラウザへの出力をEUCにしているのにフォームからの入力結果がシフトJISで送られてくるような場合(または、この逆の場合)です。

この場合でも「mbstring.http_input=auto」にしていれば、たいていの場合、PHPが自動判別して入力結果を internal_encoding の文字コードに変換してくれます。

しかし、この自動判別に失敗することがまれにあります。短い2バイト文字列を入力したような場合に判別ミスをすることがあるようです。

したがって、漢字を入力するフォームでは以下のようにダミーで送信するようにすると、この判別ミスがなくなります。

<input type=hidden name=dummyInput value="あいうえお眉幅">
(せにょーるさんのヒントから「あいうえお眉幅」がより正確に文字コードを判別するようです)


データベースを利用する場合の注意

データベースを利用する場合、internal_encoding の設定値とデータベースの文字コードが違っていると、データベースとのI/Oのたびにスクリプトで文字コードを変換する必要があります。これは、実際にやってみると非常に面倒です。したがって、データベースを利用する場合は、できるなら internal_encoding の設定値とデータベースの文字コードを合致させるようにしましょう。

たとえば、MySQLを利用する場合なら「default-character-set=sjis」としたら「mbstring.internal_encoding = SJIS」とします。
「default-character-set=ujis」としたら「mbstring.internal_encoding = EUC-JP」とします。

話は変わりますが、SQL文を動的に生成するとき、addslashes関数を利用するようにしましょう。
$str = "\\1280";
$SQL = "SELECT * FROM テーブル WHERE フィールド='$str'";
このスクリプトでは、フィールドが\1280のレコードをセレクトしているように見えますが、正しく動作しません。

$str = "\\1280";
$SQL = "SELECT * FROM テーブル WHERE フィールド='" . addslashes($str) . '";

この場合は、フィールドが\1280のレコードがセレクトされます。

なお、MySQLではaddslashesの代わりにmysql_escape_stringを使うほうがより良いと思います。

戻る

Copyright©2001-2014 釣ったよ! All Right Reserved.    sg@tsuttayo.jpn.org