Tag Archives: TSV

[PHP]文字化けせずにCSVファイルを読み込み、配列に変換する

Facebook にシェア
Delicious にシェア
LINEで送る
Pocket

CSV ファイルを読み込むとき、最もシンプルな方法は次のようなものです。

<?php
$csv  = array();
$file = 'test.csv';
$fp   = fopen($file, "r");

while (($data = fgetcsv($fp, 0, ",")) !== FALSE) {
  $csv[] = $data;
}
fclose($fp);

var_dump($csv);

fgetcsv() を用いて単純に一行ずつ処理しています。
プログラム側と CSV ファイルの文字コードが同じであればこの方法でもいいのですが、エクセルなどで作られた CSV あるいは TSV(タブ区切り)などは、Shift-JIS で保存されている場合が多いため、PHP側が UTF-8 で記述されている時、上の方法で文字コードを変換するには、mb_convert_variables() を使って再帰的に処理するか、ループ内で一行ずつ mb_convert_encoding() していくことになってしまいます。

しかしそのやり方はかなり無駄が多いので、file_get_contents() でファイル内容を取得し、一旦文字コードを変換した一時ファイルを作ってから、それを CSV ファイルとして再度読み込みます。

<?php
setlocale(LC_ALL, 'ja_JP.UTF-8');

$file = 'test.csv';
$data = file_get_contents($file);
$data = mb_convert_encoding($data, 'UTF-8', 'sjis-win');
$temp = tmpfile();
$csv  = array();

fwrite($temp, $data);
rewind($temp);

while (($data = fgetcsv($temp, 0, ",")) !== FALSE) {
	$csv[] = $data;
}
fclose($temp);

var_dump($csv);

このやり方を使えば mb_convert_encoding() を一回使うだけでエンコードの異なる CSV ファイルの読み込みができます。
tmpfile() がどこに一時ファイルを作成するかは sys_get_temp_dir() を使って調べられます。もし tmpfile() の結果が false の場合はそのフォルダに適切なパーミッションや所有者が設定されているか確認して下さい。

TSV 形式を読み込む場合でも fgetcsv() で読み込むことができます。

fgetcsv($temp, 0, "\t")

SplFileObject を利用する方法でも同様に読み込むことが出来ます。PHP 5.1 以上で利用できるクラスですが fgetcsv() よりも高速に動作します。
通常では文字列やファイルストリームを SplFileObject に変換することは出来ないので、一時ファイルのメタデータからファイルパスを取得して読み込みます。

<?php
setlocale(LC_ALL, 'ja_JP.UTF-8');

$data = file_get_contents("test.csv");
$data = mb_convert_encoding($data, 'UTF-8', 'sjis-win');
$temp = tmpfile();
$meta = stream_get_meta_data($temp);

fwrite($temp, $data);
rewind($temp);

$file = new SplFileObject($meta['uri']);
$file->setFlags(SplFileObject::READ_CSV);

$csv  = array();

foreach($file as $line) {
    $csv[] = $line;
}

fclose($temp);
$file = null;

var_dump($csv);

fclose() を使った段階で一時ファイルは自動的に消えてしまいますので不要になった SplFileObject も破棄しておきます。

Posted in PHP | Tagged , , | Leave a comment