前回の記事、「ApacheでPHPを動かす」で構築したサーバーにフリーダムウォーズの素材メモサイトと同じデータを置いてみたところ先頭の漢字のみ文字化けするという現象に遭遇しました。
問題について
どのような現象が起こっているかと言いますと・・・
こんな感じで、「甲型」で検索を行うとアルファベットから始まっている素材名は正しく表示されているものの、マルチバイト文字(カタカナや漢字)から始まっている素材名は検索にヒットせず、アルファベットが素材名の途中に入っている素材は検索に引っかかるものの、先頭から続く漢字やカタカナを表示できず、入手場所の「報」や「資」や「敵」といった漢字が全く表示されないという結果になりました。
なお、入力元は全てUTF-8、PHPのmbstring、default_charsetもUTF-8で統一しています。
また、検索処理として内部的に何をしているかと言いますと、csv形式のデータファイルを開いて「fgetcsv」でデータを取得、素材名が存在するデータに対して、検索語が存在するデータを探して、検索語に一致するデータをimplodeで再結合してJavaScriptの1データとして出力。ということをしています。
原因
fgetcsvのPHPマニュアルの公式サイトの「注意書き」を確認すると答えヒントが載っていました。以下は引用です。
注意:
この関数はロケール設定を考慮します。もし LANG が例えば en_US.UTF-8 の場合、 ファイル中の 1 バイトエンコーディングは間違って読み込まれます。
わかってみると、どうということはないのですが気付くまではなかなか大変です。
ということで、自分の環境でロケールを調べました。
# locale LANG=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" ・・・ (省略) ・・・
ビンゴでした。
localeのLANGの結果が「en_US.UTF-8」になっているため、マルチバイトを正しく読み出せなかったようです。
と・・・
本来なら、このまま解決に行きたいところでした。
しかし、公開しているフリーダムウォーズの素材サイトのサーバーのlocaleを調べると「en_US.UTF-8」になっており・・・どうも原因は別にありそうな気がしてきました。
そこで、phpinfoを確認していると「Environment」の「LANG」が「C」になっており、公開しているサーバーの設定は「en_US.UTF-8」になっていたため、私の環境の問題はこちらが原因のようです。
解決方法
解決方法には2つあります。
一つはHTTPサーバーの”LANG”環境変数を書き換える事。
もう一つはソース上にlocale設定の処理を追加する事です。
解決策1:環境変数を書き換える
ターミナルから直接、設定ファイルの編集が可能な方は、こちらの修正を行うのが良いと思います。
編集するのは「/etc/sysconfig/httpd」です。
# vi /etc/sysconfig/httpd ---------- 以下 ファイルの内容 ---------- ・・・ (省略) ・・・ #HTTPD_LANG=C HTTPD_LANG=en_US.UTF-8 ・・・ (省略) ・・・
初期状態では「HTTPD_LANG=C」がコメントアウトされて書かれています。
この行をコピーしてCの代わりに「HTTPD_LANG=en_US.UTF-8」か「HTTPD_LANG=ja_JP.UTF-8」と記入し、httpdのサービスを再起動(コマンド:service httpd restart)することで漢字を正しく扱えるようになります。
ちなみに、httpd.confに「SetEnv LANG ~」と書くことによる変更はできないようです。
解決策2:ソースコード上でlocaleを変更する
プログラムを色々なサーバーで流用するような場合(ソースコードを公開していたりする場合)などは、以下のように処理にlocale設定を組み込むのが良いと思います。
<?php setlocale(LC_ALL, 'ja_JP.UTF-8'); $fp = fopen("file.csv", "r"); // ・・・ (以下省略) ・・・
どちらの解決策でも私の所では問題なく動くようになりました。(en_US.UTF-8に設定しても問題ありませんでした)
トラックバック & ピンバック