27 August 2008

(PHP5) fgetcsvでSJISの読み込み不可

PHP5のfgetcsvで、Shift JISエンコードの日本語で、ダブルクオーテーション(”……”)で囲まれていない文字列の読み込みに失敗する。

テストしたCSVファイルは次の通り

名前, 住所, 太郎, 東京, 次郎, 神奈川, "名前", "住所", "花子", "千葉", "恵子", "埼玉", DATA, 2008-08-16, 02:22:21, A93B809474C110B0F5D5270563454066, DATA, 2008-08-13, 02:12:53, FA265CBF72815386500DAD9F90EE48CC, DATA, 2008-08-10, 12:41:50, DD4130EDF126C2192B265E6B8DB865B3,

読み込み用PHPスクリプト (スクリプトはSJISで保存。ApacheサーバはUTF-8がデフォルト)

<?php mb_language('Japanese'); mb_internal_encoding('Shift_JIS'); mb_http_output('Shift_JIS'); //setlocale(LC_ALL, "ja_JP.SJIS"); //setlocale(LC_ALL, "ja_JP.UTF-8"); setlocale(LC_ALL, "ja_JP.eucJP"); $hFile = fopen('read_csv_data.csv', 'r'); if($hFile == FALSE) { // ファイルオープンエラー die; } while($aryData = fgetcsv($hFile)) { print('<p>'); foreach($aryData as $value) { print(mb_convert_encoding($value,'SJIS', 'euc-jp').', '); // print(mb_convert_encoding($value,'SJIS', 'UTF-8').', '); // print($value.', '); } print("</p>\n"); }

CSVファイルがUTF-8またはEUCエンコードの場合
  → 文字列を "…" で囲っても、囲わなくても読み込み可能
CSVファイルがShiftJISエンコードの場合
  → 文字列を "…" で囲った場合のみ読み込み可能
※文字の種類によっては、読み込めないものもあるらしい (Google検索での情報)

つまり、PHP5のfgetcsvは使えんということか…

文字列が"…"で囲われていない場合のみなら、次のような簡単な代替手法がある

while(!feof($hFile)) { $sTmp = fgets($hFile); $aryData = explode(',', $sTmp); print('<p>'); foreach($aryData as $value) { print(mb_convert_encoding($value,'SJIS', 'euc-jp').', '); // print(mb_convert_encoding($value,'SJIS', 'UTF-8').', '); // print($value.', '); } print("</p>\n"); }

あるいは、「PHP5でfgetcsvが正常に動作しない」に掲載されている、次の関数を用いるか。

function fgetcsv_reg (&$handle, $length = null, $d = ',', $e = '"') { $d = preg_quote($d); $e = preg_quote($e); $_line = ""; while ($eof != true) { $_line .= (empty($length) ? fgets($handle) : fgets($handle, $length)); $itemcnt = preg_match_all('/'.$e.'/', $_line, $dummy); if ($itemcnt % 2 == 0) $eof = true; } $_csv_line = preg_replace('/(?:\r\n|[\r\n])?$/', $d, trim($_line)); $_csv_pattern = '/('.$e.'[^'.$e.']*(?:'.$e.$e.'[^'.$e.']*)*'.$e.'|[^'.$d.']*)'.$d.'/'; preg_match_all($_csv_pattern, $_csv_line, $_csv_matches); $_csv_data = $_csv_matches[1]; for($_csv_i=0;$_csv_i<count($_csv_data);$_csv_i++){ $_csv_data[$_csv_i]=preg_replace('/^'.$e.'(.*)'.$e.'$/s','$1',$_csv_data[$_csv_i]); $_csv_data[$_csv_i]=str_replace($e.$e, $e, $_csv_data[$_csv_i]); } return empty($_line) ? false : $_csv_data; }