【PHP実践|実務向け】PHPで文字コードが異なるCSV/TSVファイルを効率的に読み込むテクニック

導入

実務において、外部システムやExcelから出力されたCSV/TSVファイルを読み込む際、最も頭を悩ませるのが「文字コードの不一致」です。PHPは主にUTF-8で記述しますが、Windows環境由来のファイルはShift-JIS(sjis-win)であることが多く、そのまま読み込むと文字化けが発生します。本記事では、メモリ効率を考慮しつつ、スマートに文字コード変換と読み込みを行う手法を解説します。

基礎知識

PHPでCSVを扱う際、一般的には fgetcsv() 関数を使用します。これはストリームから1行ずつ読み込み、配列に変換する便利な関数です。しかし、文字コードが異なる場合、ループ内で毎回エンコード変換を行うのはパフォーマンスの低下を招きます。そこで、tmpfile() を活用した「一時ファイル作成」という手法が有効です。これは、メモリ上に変換済みのデータを書き出し、それを再度PHPのストリームとして扱うことで、ファイルシステムへの余計な書き込みを抑えつつ安全に処理する方法です。

実装/解決策

効率的な実装手順は以下の通りです。
1. ファイルの内容を file_get_contents() で取得する。
2. mb_convert_encoding() で一括変換する。
3. tmpfile() で一時ファイルを作成し、変換後のデータを書き込む。
4. SplFileObject を使用して読み込む(fgetcsv() よりもオブジェクト指向的かつ高速に処理可能)。

サンプルプログラム

以下のコードは、Shift-JISのファイルを読み込み、UTF-8として配列に格納する実用的なテンプレートです。

setFlags(SplFileObject::READ_CSV);

$csvData = [];
foreach ($file as $line) {
// 空行や不正な行をスキップする実務的な判定
if ($line === [null] || empty($line[0])) continue;
$csvData[] = $line;
}

// 後処理
$file = null; // SplFileObjectを閉じる
fclose($temp); // 一時ファイルを削除

var_dump($csvData);
?>

応用・注意点

・TSVを扱う場合:SplFileObject::setCsvControl() を使用して、区切り文字を「\t」に指定するだけで対応可能です。
・メモリ制限:巨大なファイルを扱う場合、file_get_contents() で一度にメモリへ読み込むとメモリ不足(Fatal Error)になる可能性があります。その場合は、ストリームフィルター(stream_filter_append)を使用して、読み込みながら動的に変換するアプローチを検討してください。
・パーミッション:tmpfile() でエラーが出る場合は、PHPが実行されている環境の `/tmp` ディレクトリの権限を確認してください。基本的には適切な所有者が設定されていれば問題なく動作します。

タイトルとURLをコピーしました