XAMPP環境における日本語ファイル名の文字化け解決法

前置き

WebページのPHPコードをUbuntuで書いているのですが、テスト環境としてxamppでも動かすことになりました。
開発をスムーズに進めるために、Linuxとxamppで同一ソースを使える必要があります。



XAMPPで書き出し、読み込みの両方で文字化けが発生していることがわかる



xampp環境は、Ubuntuよりも前衛的というか、Ubuntu環境がスルーする非推奨な関数などに対して敏感なようです。Ubuntu環境では問題ないPHPコードでも、エラーメッセージを出してくれます。
(面倒ではありますが)ソースコードから非推奨な関数や書き方を取り除く良い機会だとも思ったので、エラーメッセージに対してひとつずつソース修正を行うことにしました。
たぶん、xampp でデバックするコツは、検索ワードを「エラーメッセージ+エラーを起こした関数名」にすることかなと思います。
たいていの問題は、解決法がWeb上にありました。検索すればこれぞというページが出てくるので楽でした。


ところが、「日本語ファイル名の取り扱い」で、突然立ち往生することになりました。
xammp環境でPHPを使ってファイルを読み書きすると、ファイル名が文字化けします。この現象について書かれたページを探したのですが、見つけた対処法がどれも効きません。

・「php.iniを書き換える」という対処法があるのですが、自分の環境では上手く行きませんでした。
また、できれば変更はプロジェクトディレクトリの中で完結させて(バージョン管理システムで管理できるから)、環境設定はできるだけデフォルトから変更せずに済むほうが良いです。

・解決法の中には「出力をSJISにします」というものもありました。
PHPの内部エンコーディングはUTF-8が基本であり、UTF-8とSJISをソース内で混在させたら、それこそ私の手に負えない複雑なコードになりそうです。
それを避けようと思ったら、すべてをSJISに統一するしかありません。index.phpもHTMLソースもcharsetも、すべてSJISを指定して書くことになります。でも、この解決法は完全に「Windows環境で開発する」ことを意味していると思います。
わたしの場合、メインの開発環境はUbuntu Linuxで、サーバもUNIXの予定です。Linux環境でテキストはUTF-8ですし、HTMLソースやcharsetを書き換えるのも面倒です。

なんにせよ、文字化けの原因は文字コードです。

それで試行錯誤の結果、自分なりの方法を見つけたので記事にすることにしました。
 結論としては、「ファイル名の文字コードをmb_convert_encoding ()関数で相互変換する。PHP内部ではUTF-8を使い、Windows上のファイル名はCP932を使う」です。


コードサンプル

xampp(Windows)でファイル読み書きするためのPHPコード

 例:xampp環境でファイルを読み書きする(開発中コードから切り出したもの)
ファイルを書く場合
//POSTでファイルを受け取る部分
(ブラウザから受け取ったファイル名を、Windowsディレクトリへ書き出すためにWindows環境の文字コードへ変換)
  $localFN = mb_convert_encoding
                ($_FILES["upfile"]["name"],"CP932", "UTF-8");
  if (move_uploaded_file($_FILES["upfile"]["tmp_name"],
          "imgs/" . $localFN)) {

      (略)
  }

ファイルを読む場合
readdir()関数でフォルダの中身を読み込むと、
  while (false != ($imgName = readdir($dir))){
    $imgName = mb_convert_encoding($imgName, 'UTF-8', 'CP932');

      (略)
  }



Ubuntuとxampp(windows) 環境で動くようにする



ファイルを読む場合
  if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ){
    //xampp対応。Windows系の場合、文字コードを変換。
    $imgName = mb_convert_encoding ($imgName, 'UTF-8', 'CP932');
  }


ファイルを書く場合
他の箇所で使う予定もあったので、内部コードから環境の文字コードへの変換は、関数にまとめました。
  $localFilename = conv_str_to_local($_FILES["upfile"]["name"]);
    
    //環境を判定して文字コード変換(内部コード->環境の文字コード)
  function conv_str_to_local ($inString){
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ){
      //xampp対応。Windows系の場合、文字コードを変換。
      $localString = mb_convert_encoding

                        ($inString,"CP932", "UTF-8");
    }else{
      $localString = $inString;
    }
      
    return $localString;
  }



この方法ならxampp環境を変更せずにソースの変更だけで済みます。他の方法より書いたり管理するのが単純です。


解説


おおまかにまとめると、
・PHPの内部エンコーディングはUTF-8
・使っているHTMLソースもUTF-8
・Windows日本語環境はCP932(≒SJIS)
よって、「Windowsファイルシステムを読み書きする際に文字コード変換の必要有り」です。

そこで、PHP内部ではファイル名をUTF-8で取り扱います。
ファイルを読み書きする際に、ファイル名を表す文字列を文字コード変換してから、ファイルを開きます。
これにより、PHP内部ではUTF-8以外の文字コードを扱うことによる問題を最小限に抑えられます。
また、変換するかどうかは、OSの種類がWindowsかどうかを判別して決めますが、「とりあえず関数に通す」だけで済むようにして、コードが複雑になるのを防ぎます。


そもそも、私の書いていたWebページの不具合は、文字コードが原因である、独立した2つの問題でした。
・POSTで取得した日本語ファイルを保存すると、文字化けしている。
・日本語画像ファイルをWebページに表示しようとすると、ファイル名が文字化けして、画像が表示できない。

読み込みの文字化けを解消(ブラウザ表示)

書き込みの文字化けを解消(アップロード)


文字化けした名前で読み書きすることは可能
(2重変換のようなことになっていなければ)


その他

今回扱ったのはファイル名の文字コードであり、テキストファイルの内容は文字コードを変更していません。
今回書いたWebページでは、テキストファイルはすべてWebページ上のテキストボックスから書き込み、PHPを通してWebページへ書き戻すので、読み書きがすべてUTF-8で完結しています。書き出したテキストファイルをメモ帳などのWindowsアプリケーションで読み書きする場合は、ファイルから読み出したテキストデータも文字コード変換する必要があるかもしれません。(TeraPadなどのUTF-8対応テキストエディタを使ったほうが楽だと思いますが。)


 「mb_convert_encoding()を使ってエラーが出るときは、mb_language("Japanese");を宣言するといいよ」と書いてあるサイト様を見かけましたが、今回の例では不要でした。

最後に参考サイト様を。
OSの判定方法は、PHPリファレンスマニュアルのphp_uname()関数から。
アップロードファイルの書き出しは、サイト様のXAMPP(Apache+PHP+MySQL+Mercury)での文字コード関連関連から。


0 件のコメント:

コメントを投稿

Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換する

 Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換することができるが、上手く変換されない場合がある。   以前作った魔法陣イラスト素材をイラスト素材ストックサイトへ登録しようと思い立ち、改めて素材用にラスタライズ変換をかけようとした。   ラスタライズ変換...