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;
}