こんにちは。
先日、CSVをPHPで読み込む処理を書いていたときに、ちょっと驚くことがありました。
複数文字コード指定で思わぬ判定
CSVで商品情報などを一括登録する機能を作成していたところ、
いつものように、アップロードされたCSVを取り込む時にカラムの整合性をチェックしていたら、
元のカラムと変更していないのにエラーになってしまいました。
$csv = file_get_contents('data.csv');
$enc = mb_detect_encoding($csv, "ASCII,JIS,UTF-8,EUC-JP,SJIS", true);
この CSV は SJIS で作られていたので、「いつもどおりSJIS と判定されるはず」と思っていました。なんかでも PHP8 で実行したら ASCII になったな……?
実際のCSVの中身は日本語も含まれているのに、何故かASCIIと認識されてしまいました……。
調べてみると、PHP8では mb_detect_encoding() の内部ロジックが変更され、複数の文字コードを指定している場合、最初に「安全に読める」と判断されたものが返る傾向があります。
つまり、SJISも指定していても、文字コード上問題がないASCIIが優先され、結果としてASCII判定になったようでした。
実務での対応
文字化けが起きるとCSVの読み込み処理が止まってしまうので、今回は対応策として複数文字コード指定をやめ、ファイルごとに文字コードを明示的に指定することにしました。
// SJIS ファイルなら $csv = file_get_contents('data.csv');
$csv = mb_convert_encoding($csv, 'UTF-8', 'SJIS');
この方法で、文字コードの判定に悩むことなく、安定してCSVを読み込めるようになりました。
まとめ
- PHP8 では mb_detect_encoding() の複数文字コード指定で、期待と違う判定が返ることがある
- CSVの中身次第では、SJISファイルでもASCII判定される場合がある……
- 安定させるには、複数文字コード指定をやめて、ファイルごとにエンコーディングを明示する
ちょっとした仕様の変化ですが、業務で使うCSVの読み込みでは影響が大きく、予想外の結果に戸惑いました。