fgetcsvの不具合

9月 5th, 2007 tocmoc

2008.4.28 修正
----
PHPでcsvを読み込む場合,fgetcsvという関数が提供されているので,これを使うと楽だ.

しかし,ソフトによってはCSVを保存すると,ファイルの先頭に自動的にBOM(Byte Order Mark)が付与される場合がある.BOMは,ファイルのエンコーディングスキームを認識するためのもので,ファイルの先頭にそのための識別子を記述しておく.というものだ.

UTF-16であれば,BOMは先頭2バイトにデータが付与され,
UTF-8の場合は,BOMは先頭3バイトにデータが付与される.

UTF-16LE(Little Endian)
FF FE 
UTF-16BE(Big Endian)
FE FF
UTF-8
EF BB BF

PHPでCSVの処理をする場合,fgetcsvを使っていると,このBOMのせいでデリミタや二重引用符がうまく処理できない場合がある.私の場合は,データ先頭の二重引用符が二重に処理されてしまった.


"1","2","3","4"......
のはずが,
""1"","2","3","4"......
となってしまった

対処法としては,BOMを削除したファイルを作るか,PHPのプログラム内でBOMを検出して読み飛ばすようなコードを書けば良いということになる.

ファイルを指定してBOMを削除するフリーソフト
non-BOM
http://www.vector.co.jp/soft/win95/util/se218158.html

PHPプログラム内で自力でどうにかしたい場合には,fegtcsvに入れる前にファイルポインタの先頭3文字をチェックしておく.

PHP:
  1. function bomcheck($FilePointer)
  2. {
  3.  $bom = fread($FilePointer,3);
  4.  if(strcmp($bom,"\xEF\xBB\xBF") == 0 ){
  5.   // UTF-8 BOM found.
  6.   print "---found\n";
  7.  }else{
  8.   // not found.
  9.   rewind($FilePointer, 0);
  10.   print "---not found\n";
  11.  }
  12.  return $FilePointer;
  13. }

使う時には,次のような感じ

PHP:
  1. $fp = fopen("hogehoge.dat", "rb");
  2. if($fp){
  3.  // BOMチェック
  4.  $fp = bomcheck($fp);
  5.  
  6.  while(($data = fgetcsv($fp, 1000))) {
  7.   print $data[0]."\n";
  8.  }
  9.  fclose($fp);
  10. }