[PHP] csv・tsvファイルの行末の改行コードが認識されない
SplFileObject
(READ_CSV)を使ってtsvファイルを読み込んだところ、全ての値が1行として扱われた。。。csvファイルで試しても同じ。tsvファイルの行末の改行コードが認識されていなかったことが原因だった。
起きていること
b.tsvを読み込むと全ての値が1行になる# b.tsvファイル
aaa bbb ccc
a1 b1 c1
a2 b2 c2
a3 b3 c3
// PHP処理
$file = new SplFileObject("b.tsv");
$file->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD);
$file->setCsvControl("\t");
foreach ($file as $line) {
echo "line No:".$file->key();
echo "
";
echo "";
var_dump($line);
echo "
";
}
実行結果
line No:0 array(9) { [0]=> string(3) “aaa" [1]=> string(3) “bbb" [2]=> string(6) “ccc a1" [3]=> string(2) “b1" [4]=> string(5) “c1 a2" [5]=> string(2) “b2" [6]=> string(5) “c2 a3" [7]=> string(2) “b3" [8]=> string(2) “c3" }
改行されるべきところが1つの要素になってしまっている…
line No:0 array(9) { [0]=> string(3) “aaa" [1]=> string(3) “bbb" [2]=> string(6) “ccc a1" [3]=> string(2) “b1" [4]=> string(5) “c1 a2" [5]=> string(2) “b2" [6]=> string(5) “c2 a3" [7]=> string(2) “b3" [8]=> string(2) “c3" }
改行されるべきところが1つの要素になってしまっている…
b.tsvファイルはずっとMacのテキストエディットで開いていてテキストエディットだと改行されているが、Atomで開くと改行がない
ターミナルで改行コードを調べる
$ file b.tsv
b.tsv: ASCII text, with CR line terminators
改行コードが「CR」になっている
原因
fgets()やfile()などPHPのファイル操作系関数では、「CR+LF」と「LF」は改行として扱われるが、「CR」は改行として認識しない
解決方法
auto_detect_line_endings
をOnにするとCRも改行コードとして認識させることができる(デフォルトではOffになっている)
ini_set('auto_detect_line_endings',true);
auto_detect_line_endings
とは
公式ドキュメント 実行時設定 onにした場合、PHPは fgets() および file() により読み込まれたデータを評価し、UNIX、MS-DOS、Machintoshの行末 表記を使用しているかどうかを調べます。
これにより、PHPがMacintoshシステムと相互運用できるようになりますが、 デフォルトはOffとなっています。これは、最初の行の行末表記を検出 する際にごく僅かな性能劣化があるためと、UNIXシステムのもとで復改 文字を項目セパレータとして使用している人が従来のバージョンと互換 性がない動作であると感じる可能性があるためです。
公式ドキュメント fgets 注意: マッキントッシュコンピュータ上で作成されたファイルを読み込む際に、 PHP が行末を認識できないという問題が発生した場合、 実行時の設定オプションauto_detect_line_endings を有効にする必要が生じるかもしれません。
preg_replace("/\r\n|\r|\n/", $to, $string);
とかで改行コードを変換する方法もあるが、それだとfile_get_contents
で一度ファイルの中身を読み込んだりする必要があるので微妙