Windows上PHPで、SJISの場合とUTF-8の場合があるCSVのデータを行単位で取得する

Windows のPHPは、もはやバグと言っても良いと思われる致命的な問題をかかえています。それはUTF-8 のCSVを fgetcsv / str_getcsv などのCSV読み込み関数を使って読み込むと、列が正しく分割できず、2つの列が一つにまとまって認識したりして、正しく読めないのです。

SJIS(MS932)ならそれなりに読めるのですが、データがUTF-8の場合もあり、それを一旦SJISにするわけにもいきません。SJISでは扱える文字が限られてしまいますので。そこで、データがSJISとUTF-8の両方の場合があるケースでは、UTF-8にして保存して、それをCSVとして読み込もうとするわけですが、くだんのバグで正しく読めないとなります。

厳密にはバグではなく、ロケールの問題で、PHPのこの処理はロケールの影響を受けるとされているので、正しいロケールが設定されていれば、きちんと読めます。 例えば、setlocale(LC_ALL, “C”);とすれば、その後は正しく読めます。しかし、setlocale は、PHP全体に影響があり、今実行している処理だけ変更できるわけではありませんから、うかつに実行できないのです。ですが、fgetcsv / str_getcsv などで引数で指定できるわけでもなく、これはもうバグといっても過言ではない設計上の問題でしょう。

CSV処理系(ファイル処理系)の関数に文字コード指定の引数があれば良いはずですが、なぜかPHPは、そういうものはなく、読み込んだ後に自分で、mb_convert_encodingで変換しなければならないものばかりです。

そして、UTF-8 のCSVを読み込んで、メモリ上で行分割したい場合には、更に悩ましいことに、fgetcsv は、カラム分割までしてしまうという点です。つまり取得できるのは、各行を絡む分割した配列であって、CSV1行ではありません。その配列をCSV1行にしたいと思ったら、一番簡単な方法は、implode を使って “,” で連結する方法ですが、これは、データに改行やクォートを含んでいたりすると、まともに処理できなくなることは明白です。

そこで、配列をCSV1行分のデータとしてまとめる関数は無いかと探すのですが、ありません。あるのは、fputcsv だけで、これは、配列データをCSVとしてファイルに1行分出力する関数であって、1行分の文字列を得られるわけではありません。

結局、ばかばかしいと思いながらも、1行読み取った配列を1行だけファイルに書き出して、そのファイルを読み込んで1行分の文字列にするという操作を全部の行で繰り返して、最終的にCSV行の配列を得ることになります。

もっと賢いやり方はないものでしょうか?

カテゴリー: ZAKKI パーマリンク