PHPのfgetcsv
はロケールに依存しており、ロケールの環境設定次第では読めないデータが存在するという情報がいくつか存在する。
fgetcsvのロケール問題に言及した記事
まずはどのような情報があるか見てみよう。
【php】fgetcsv()はロケールの設定に依存する at softelメモより:
「”名前”,”住所”,”岐阜”,”愛知”,”東京”」は読めるけど、「名前,住所,岐阜,愛知,東京」は読めない。
fgetcsv関数を文字化け対応 setlocaleの文字コード指定 - [PHP + PHP] ぺんたん infoより:
ID,名前,都道府県ID,電話番号or携帯番号
これをfgetcsv()関数で読み込むと
Array( [0] => ID, [1] => , [2] => ID, [3] => or携帯番号, )
となります。
PHP5 fgetcsv 日本語文字が消える場合 | Bamboo lath 日々の記録より:
今回問題となった元のデータはこれ
9033,裏面その他(モノクロ),"9033,9034,9035,9036,9037,9038,9039,9040",,サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。
今回問題となった箇所はここ
サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。
消えた状態
<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。
見つけられた範囲で読めないデータを提示している情報源は以上の3つだけだったが、データの提示なしにsetlocale
を使うことを推奨する記事はかなりの数があった。
本当にfgetcsvはsetlocaleしないとだめなのか?
手元のPHP環境(PHP 7.1)ではsetlocaleをしなくてもfgetcsvで問題なくデータが読み込めていたので、 fgetcsvはsetlocaleしないと読めないデータがあるというのは本当なのかという疑問が生まれた。
一応、macOS上のPHPだけでなく、Alpine Linux(localeがない)やUbuntu(localeがC)でも試したが再現しなかった。
また、SJISのCSVでも再現しなかった。
仮説: PHPのあるバージョンからロケール問題がなくなった
fgetcsvでロケールの調整が必要とする記事がPHP5時代のものが多かったので、もしかするとPHPのあるバージョンから上で取り上げたようなロケール問題が解消されているのではないかと考えた。
※ロケール問題が解消されたというのは、setlocale
をしなくてもデータが読み込めるということで、fgetcsv
がロケール依存でなくなったという意味ではない。
検証
PHPバージョンでの差異を検証するために、次の検証コードを用意した。ロケールC
でUTF-8のCSVデータを読み込むロジックになっている。
<?php
// テスト用CSVの用意
$filename = tempnam(sys_get_temp_dir(), 'CSV');
file_put_contents(
$filename,
"名前,住所,岐阜,愛知,東京\n". // ロケールが正しくないと読めないとされる行
'ID,名前,都道府県ID,電話番号or携帯番号'."\n". // ロケールが正しくないと読めないとされる行
'9033,裏面その他(モノクロ),"9033,9034,9035,9036,9037,9038,9039,9040",,サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。'
);
// テスト
setlocale(LC_ALL, 'C'); // ロケールをCにする
$file = new SplFileObject($filename, 'r');
$file->setFlags(SplFileObject::READ_CSV);
var_dump(iterator_to_array($file, false));
検証範囲はPHP5.2.1から7.3.1とする。
検証結果
上記の検証コードを複数のPHPバージョンでコードを実行できる環境で実行してみたところ、次の結果が得られた。
- PHP 5.3.7 - 5.6.38, 7.0.0 - 7.3.1: 再現せず
- B: PHP 5.2.1 - 5.3.6: ロケール問題が再現した
array(3) {
[0]=>
array(5) {
[0]=>
string(6) "名前"
[1]=>
string(6) "住所"
[2]=>
string(6) "岐阜"
[3]=>
string(6) "愛知"
[4]=>
string(6) "東京"
}
[1]=>
array(4) {
[0]=>
string(2) "ID"
[1]=>
string(6) "名前"
[2]=>
string(14) "都道府県ID"
[3]=>
string(26) "電話番号or携帯番号"
}
[2]=>
array(5) {
[0]=>
string(4) "9033"
[1]=>
string(33) "裏面その他(モノクロ)"
[2]=>
string(39) "9033,9034,9035,9036,9037,9038,9039,9040"
[3]=>
string(0) ""
[4]=>
string(156) "サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。"
}
}
array(3) {
[0]=>
array(5) {
[0]=>
string(0) ""
[1]=>
string(0) ""
[2]=>
string(0) ""
[3]=>
string(0) ""
[4]=>
string(0) ""
}
[1]=>
array(4) {
[0]=>
string(2) "ID"
[1]=>
string(0) ""
[2]=>
string(2) "ID"
[3]=>
string(14) "or携帯番号"
}
[2]=>
array(5) {
[0]=>
string(4) "9033"
[1]=>
string(0) ""
[2]=>
string(39) "9033,9034,9035,9036,9037,9038,9039,9040"
[3]=>
string(0) ""
[4]=>
string(111) "<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。"
}
}
結論
fgetcsvはsetlocaleしないと読めないデータがあるというのは、PHP5.3.6以下のバージョンで、PHP7の環境が普通の今日現在ではsetlocaleしなくても問題はなさそう。