SplFileObject で Shift_JIS の CSVファイルを読み込む


PHP で CSVファイルといえば、fopen() で指定して fgetcsv() でデータを取得する方法がすぐに頭に浮かび、検索してもその方法が多くヒットされます。で、途中に改行が含まれる CSVファイルを取り扱う場合は・・・。お断りしたいくらいです。

そんな思いに反して「途中に改行が含まれる CSVファイル(Shift_JIS)をPHP(UTF-8)でデータベース(utf8)に保存」というステキなご要望があり、奮闘した記録をここに載せようかと。

SplFileObject

冒頭にも記載した通り、PHP で CSVファイルといえば、fgetcsv() ばかりなのですが、そんな中、見つけたのが「SplFileObject クラス」。そうです。オブジェクト指向です! 5.1 から! 知りませんでした。(^^;
早速、様々なサンプルを拝見し、次のように組んでみました。

$file = new SplFileObject( 'exp.csv' );
$file -> setFlags( SplFileObject::READ_CSV );
$file -> setCsvControl( ',', '"' );
foreach( $file as $key => $line ){
	foreach( $line as $str ){
		$records[$key][] = strlen( $str ) > 0 ? mb_convert_encoding( $str, 'UTF-8', 'Shift_JIS' ) : '';
	}
}
var_dump( $records );

上のスクリプトで、ほとんどは意図した通りに動きました。「ほとんど」・・・です。
希に隣り合う項目が結合されることが見受けられます。
具体的には、「”試験”,”動作”,”042-812-7728″,”神奈川県”」が正しいところ「”試験”,”動作”,042-812-7728″”,”神奈川県”」となり「”動作”,042-812-7728″”」が1つのデータと認識されてしまうことがあります。

原因は、Shift_JIS

ローカルでCSVファイルを UTF-8 で保存し、上のスクリプトで mb_convert_encoding を外して動作チェックを行ったところ、全て意図した通りに動きましたので、原因は文字コードの変換部分。しかも、上記の現象ですから、疑うのは1~2行目の部分。PHP が UTF-8 で CSVファイルが Shift_JIS なのだから、無理があるのは当然かと。

$file = new SplFileObject( 'exp.csv' );
$file -> setFlags( SplFileObject::READ_CSV );

対応策

はじめに一時保存用のファイルを作成し、サーバーにアップ。適切なパーミッションに設定。
csvファイルを file_get_contents で読み込み、文字コードを変換。その後、一時保存用のファイルに保存。
一時保存用のファイルで上のスクリプトを実行させる。
一時保存用のファイルの中身を空にする。

Excel や Access からの CSVファイルは、UTF-8 にして欲しいと思うこの頃です。

[mokurenCB]