PHPの文字コードに関する問題は厄介だ。
というのは、多くの書籍がスクリプトファイルの解析処理はSJISに対応していないという点を伝えていないからだ。
ここは、対応していないと言い切ってしまいたい。一応、再コンパイルすればSJIS対応化することもできるが、Windows環境の場合、その道は長く厳しい。
POSIX系OSの開発を実務でやった事がある程度にUnix系の構成に知識がある俺からしても、どうよって感じの複雑さの上、ドキュメントが間違っており(厳密には古い)、必要なツールのリンクが切れている上、コンパイルが通らないという状況。環境がCywgwinだからというのもあるのかもしれないが。
しかも、何とか動くものが用意できても、この世のほとんどのフレームワーク、ライブラリはスクリプト解析がUTF-8環境下(つまりデフォルト)でのEUC、もしくは、SJISを前提として記述されており、色々文字を扱う部分でハックしているので結局は純粋自作環境以外は.htaccessに、
php_value default_charset UTF-8
php_value mbstring.language neutral
php_value mbstring.internal_encoding UTF-8
php_value mbstring.script_encoding UTF-8
とか記載して一般配付版と同じ扱いにする必要がでてくるのだ。こうしないとほとんどのブログシステム、フレームワークは動作しなくなる。
上記のオプションのmbstring.script_encodingは拡張コンパイルによって使う事ができるようになるオプションなので、PHPには3つも文字コード設定が分散しているのである。
これらの違いを明確にしているサイトがなかったので、一応、こう考えておけばOK的な説明をする(つまり厳密化どうかの保証はない)。
・default_charset
HTMLヘッダの要素、Content-Type: text/htmlのcharsetの値の指定
・mbstring.language
HTTP、SMTP等の文字送信が行なわれるプロトコルの文字コードの指定
・mbstring.script_encoding
文字列を処理するときの文字コードの指定
とまあ、こんな感じ。
ここで誤解が生じやすいのがmbstring.script_encodingで、文字列はスクリプトファイルに直接記述もするのだし、ネーミングからしてもスクリプトファイルの文字コードの指定と勘違いする。普通は。しかし、これはあくまでmb_*系の関数内で、特に指定がないときに文字列の文字コードを何として扱うかという指定なのである。
さて、ではスクリプトファイル自体の解析は、文字コードは何として行なわれるのかというとUTF-8らしい。
EUC文字コードで記載しても問題がないのは、EUCは\文字の問題がないというその存在理由そのもののおかげであり、決してPHPのスクリプト解析がデフォルトでEUCをサポートしているからではない。
では、どのような文字コードで記述するべきか。日本人なら迷うことなく、SJIS、CRLFの一択だろう。しかし、どちらも普通に参考書どおりにやったら問題がでる。そこで、どうやるかを示そう。
○SJIS
SJIS対応か見抜く方法を私は次のように口伝している。
『可能は不可能』
この"能"という漢字の下位バイトには\と同じ文字コードが含まれている。従って、SJIS非対応のC言語コンパイラ等では、
// 可能
は不可能
等とやってコンパイルエラーがでるかどうかを調べるのだ。
さらに文字列定数の定義で、
『ソニーは可能』
とやる。この"ソ"の漢字の下位バイトにも\と同じ文字コードが含まれているためPHPならば、
echo "ソニーは可能";
とやる。そうすると、能の後ろにある"がないというエラーが発生する。これは\で"がエスケープされ単なる文字として扱われてしまうからだ。
そこで、
echo "ソニーは可能.";
などとやると、スクリプトではエラーが出なくなるが、内部文字コードがSJISでない場合は"ソ"が正しく表示されない。
このように、MySQLへ"ソニーは可能"を登録して正しく取得できたらOKなど、色々な分野でSJIS対応かどうかの確認に役立ってくれる。
さて、SJISを使っていく方法だが、上記のように単に画面表示に日本語を出力したい場合は、スペースか.等で"の前が漢字で終わらないようにルール付けする。
パターンマッチングなどで、そういう不要なものを入れる余裕がない場合は、
$a = trim("可能 ");
とやって、エスケープ回避のスペースをtrimで削除して変数代入して使っていく事になる。
これなら、将来的に文字列をEUCにしようが、UTF-8にしようが問題は生じない。
○CRLF
PHPはprintf、echo等のストリームへの出力時に\nを\n\rと自動変換しない。従って、
echo "テスト\n";
等としている場合に、ファイルへリダイレクトすると、LF改行コードのファイルになってしまう。
これを回避するためには、実行環境ごとの改行コードの違いを吸収するために定義されている定数PHP_EOLを使用する。
echo "テスト" . PHP_EOL;
これで万事OK。
当然のことながら、Linux環境下でCRLFで出力させたい場合は\r\nを明示的に出力させなければいけないので注意すること。
さて余談だが、echoが,を使って、
echo "テスト" , PHP_EOL;
とできる理由もなんとなく、改行コードは付加するという方針が最初からあって、文字連結の"."ではなく、","でも行なえるようにして、
echo $text[, EOL]
という仕様として策定されたのではないかと思うね。実際にはいくつも連結可能だから、
echo $text[, $text ...]
だけどね。
というのは、多くの書籍がスクリプトファイルの解析処理はSJISに対応していないという点を伝えていないからだ。
ここは、対応していないと言い切ってしまいたい。一応、再コンパイルすればSJIS対応化することもできるが、Windows環境の場合、その道は長く厳しい。
POSIX系OSの開発を実務でやった事がある程度にUnix系の構成に知識がある俺からしても、どうよって感じの複雑さの上、ドキュメントが間違っており(厳密には古い)、必要なツールのリンクが切れている上、コンパイルが通らないという状況。環境がCywgwinだからというのもあるのかもしれないが。
しかも、何とか動くものが用意できても、この世のほとんどのフレームワーク、ライブラリはスクリプト解析がUTF-8環境下(つまりデフォルト)でのEUC、もしくは、SJISを前提として記述されており、色々文字を扱う部分でハックしているので結局は純粋自作環境以外は.htaccessに、
php_value default_charset UTF-8
php_value mbstring.language neutral
php_value mbstring.internal_encoding UTF-8
php_value mbstring.script_encoding UTF-8
とか記載して一般配付版と同じ扱いにする必要がでてくるのだ。こうしないとほとんどのブログシステム、フレームワークは動作しなくなる。
上記のオプションのmbstring.script_encodingは拡張コンパイルによって使う事ができるようになるオプションなので、PHPには3つも文字コード設定が分散しているのである。
これらの違いを明確にしているサイトがなかったので、一応、こう考えておけばOK的な説明をする(つまり厳密化どうかの保証はない)。
・default_charset
HTMLヘッダの要素、Content-Type: text/htmlのcharsetの値の指定
・mbstring.language
HTTP、SMTP等の文字送信が行なわれるプロトコルの文字コードの指定
・mbstring.script_encoding
文字列を処理するときの文字コードの指定
とまあ、こんな感じ。
ここで誤解が生じやすいのがmbstring.script_encodingで、文字列はスクリプトファイルに直接記述もするのだし、ネーミングからしてもスクリプトファイルの文字コードの指定と勘違いする。普通は。しかし、これはあくまでmb_*系の関数内で、特に指定がないときに文字列の文字コードを何として扱うかという指定なのである。
さて、ではスクリプトファイル自体の解析は、文字コードは何として行なわれるのかというとUTF-8らしい。
EUC文字コードで記載しても問題がないのは、EUCは\文字の問題がないというその存在理由そのもののおかげであり、決してPHPのスクリプト解析がデフォルトでEUCをサポートしているからではない。
では、どのような文字コードで記述するべきか。日本人なら迷うことなく、SJIS、CRLFの一択だろう。しかし、どちらも普通に参考書どおりにやったら問題がでる。そこで、どうやるかを示そう。
○SJIS
SJIS対応か見抜く方法を私は次のように口伝している。
『可能は不可能』
この"能"という漢字の下位バイトには\と同じ文字コードが含まれている。従って、SJIS非対応のC言語コンパイラ等では、
// 可能
は不可能
等とやってコンパイルエラーがでるかどうかを調べるのだ。
さらに文字列定数の定義で、
『ソニーは可能』
とやる。この"ソ"の漢字の下位バイトにも\と同じ文字コードが含まれているためPHPならば、
echo "ソニーは可能";
とやる。そうすると、能の後ろにある"がないというエラーが発生する。これは\で"がエスケープされ単なる文字として扱われてしまうからだ。
そこで、
echo "ソニーは可能.";
などとやると、スクリプトではエラーが出なくなるが、内部文字コードがSJISでない場合は"ソ"が正しく表示されない。
このように、MySQLへ"ソニーは可能"を登録して正しく取得できたらOKなど、色々な分野でSJIS対応かどうかの確認に役立ってくれる。
さて、SJISを使っていく方法だが、上記のように単に画面表示に日本語を出力したい場合は、スペースか.等で"の前が漢字で終わらないようにルール付けする。
パターンマッチングなどで、そういう不要なものを入れる余裕がない場合は、
$a = trim("可能 ");
とやって、エスケープ回避のスペースをtrimで削除して変数代入して使っていく事になる。
これなら、将来的に文字列をEUCにしようが、UTF-8にしようが問題は生じない。
○CRLF
PHPはprintf、echo等のストリームへの出力時に\nを\n\rと自動変換しない。従って、
echo "テスト\n";
等としている場合に、ファイルへリダイレクトすると、LF改行コードのファイルになってしまう。
これを回避するためには、実行環境ごとの改行コードの違いを吸収するために定義されている定数PHP_EOLを使用する。
echo "テスト" . PHP_EOL;
これで万事OK。
当然のことながら、Linux環境下でCRLFで出力させたい場合は\r\nを明示的に出力させなければいけないので注意すること。
さて余談だが、echoが,を使って、
echo "テスト" , PHP_EOL;
とできる理由もなんとなく、改行コードは付加するという方針が最初からあって、文字連結の"."ではなく、","でも行なえるようにして、
echo $text[, EOL]
という仕様として策定されたのではないかと思うね。実際にはいくつも連結可能だから、
echo $text[, $text ...]
だけどね。


