※ パクレゼルヴではWeb開発エンジニアを大募集中!詳細はこちら
Home > PHP, 未分類 > fgetcsvがphp5で文字化けする

fgetcsvがphp5で文字化けする  

2010/5/31 月曜日 20:45:08

仕事でCSVを使うことがあったんですけど、その時に悩んだことを書こうと思います。

fgetcsv()という便利な関数があるんですが、php5で使うと文字化けするんですよ。
なぜ文字化けが起きるのかと言うと、
「fgetcsvはマルチバイト文字の2バイト目に’\'があると、直後のダブルクォーテーションをエスケープするという、少なくともエクセルの仕様にはない独自解釈をする。」
らしい。
訳わかんない事言ってるなーと思いますが、要はShift-JIS形式のCSVをfgetcsv()関数でと文字化けするみたいです。

いろいろと調べた結果、
「PHP 5.0 の fgetcsv() はロケールの設定に依存します。」
とのことなので、

setlocale('LC_ALL', ja_JP.EUC_JP);

でロケールを設定してあげれば直ら・・・・ない。
ぱっと見た感じ直ってるように見えるんですけど、機種依存文字が化けてしまう。

それじゃあCSVをまずUTF-8とかに変換しちゃえば解決するんですけど、実はfgetcsvぽく動いて、しかもバグがない
関数があったので、それを使うことに。

function fgetcsv_reg (&$file, $length = null, $d = ',', $e = '"') {
	$d = preg_quote($d);
	$e = preg_quote($e);
	$_line = "";
	$eof = FALSE;
	while (!$eof && !feof($file)) {
		$_line .= (empty($length) ? fgets($file) : fgets($file, $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("'","\\'",$_csv_data[$_csv_i]);
		$_csv_data[$_csv_i]=str_replace($e.$e, $e, $_csv_data[$_csv_i]);
	}
	return empty($_line) ? false : $_csv_data;
}

この関数で、CSVを正規表現で切ってます。
使うときはこういう感じで

$new_data = array();
$header = array('name','zip','addr','tel','fax','manage','security','url');

$filename = 'CSVファイルパス';
$file = fopen($filename, "r");

while (($data = fgetcsv_reg($file)) !== false) {
	$_enc_to=mb_internal_encoding();
	$_enc_from=mb_detect_order();
	mb_convert_variables($_enc_to,$_enc_from,$data);
	$num = count($data);
	for ($c=0; $c < $num; $c++) {
		$new_data[$header[$c]] = $data[$c];
	}
	foreach($new_data as $key => $val){
		echo $key." : ".$val."<br />";
	}
}

こういう風に使えば、fgetcsv()関数とほぼ同じ動作をし、さらに文字化けもおきません。

こういうオリジナルの関数を自分でも作れるようになりたいですね。。

つっしー PHP, 未分類

  1. No comments yet.
  1. No trackbacks yet.