なぜ「CSV 用エスケープ」が必要になるのか
業務で CSV を扱うとき、よくあるのが「Excel で開ける CSV を出したい」「システム間連携用に CSV を吐きたい」というパターンです。ここで雑に文字列をカンマでつないでしまうと、すぐに壊れます。
値の中にカンマが入っている。
値の中に改行が入っている。
値の中にダブルクォートが入っている。
こういうとき、正しく「エスケープ」しておかないと、CSV として解釈できなくなります。CSV 用エスケープは、「1 セル分の文字列を、CSV 仕様に沿った安全な形に変換する」処理だとイメージしてください。
CSV の基本ルールをざっくり押さえる
CSV の細かい仕様はいろいろありますが、実務で最低限覚えておきたいルールは次のとおりです。
カンマや改行を含むフィールドは、ダブルクォート " で囲む。
フィールド内にダブルクォート " がある場合は、"" と 2 つ重ねて書く。
例えば、こういう値があったとします。
こんにちは, 世界彼は「天才」です
これをそのまま CSV に書くと、カンマやダブルクォートのせいで区切りが壊れます。正しくはこうなります。
"こんにちは, 世界""彼は""天才""です"
この「囲む」「二重にする」を自動でやってくれるのが、CSV 用エスケープです。
PHP 標準の fputcsv を知っておく
実は「自前でエスケープしなくていい」場面が多い
PHP には、CSV を正しく出力してくれる fputcsv() という関数があります。これを使えば、1 行分の配列を渡すだけで、カンマ区切り+必要なエスケープを全部やってくれます。
$row = [
'id' => 1,
'name' => '山田, 太郎',
'comment' => '彼は「天才」です',
];
$fp = fopen('php://output', 'w');
fputcsv($fp, $row); // カンマ区切りの 1 行を出力
fclose($fp);
PHPこのコードが出力するのは、例えばこんな感じです。
1,"山田, 太郎","彼は""天才""です"
カンマを含む name がダブルクォートで囲まれ、
comment 内の「」が "" に変換されているのが分かります。
もし「ファイルに書きたい」「ダウンロードさせたい」などの用途なら、まずは fputcsv() を使うのが一番簡単で安全です。
それでも「1 セルだけエスケープしたい」ことがある
自前のエスケープ関数を作る
ときどき、「配列を一気に CSV にするのではなく、1 セルずつ文字列を組み立てたい」という場面があります。そのときに使えるのが「1 フィールド用のエスケープ関数」です。
CSV のルールに従うと、やることはこうです。
値の中にカンマ・改行・ダブルクォートが含まれていたら、ダブルクォートで囲む。
中にあるダブルクォートは "" に置き換える。
これをコードにすると、こうなります。
/**
* 1 フィールド分の文字列を CSV 用にエスケープする
*/
function csv_escape_field(string $value): string
{
$needsQuote = strpbrk($value, ",\"\r\n") !== false;
if ($needsQuote) {
// ダブルクォートを "" に置き換える
$escaped = str_replace('"', '""', $value);
// 全体をダブルクォートで囲む
return '"' . $escaped . '"';
}
// 特殊文字がなければそのまま
return $value;
}
PHP使い方の例です。
echo csv_escape_field('山田太郎'); // 山田太郎
echo csv_escape_field('山田, 太郎'); // "山田, 太郎"
echo csv_escape_field('彼は「天才」です'); // 彼は「天才」です(ダブルクォートなし)
echo csv_escape_field('彼は"天才"です'); // "彼は""天才""です"
PHPここでのポイントは、「カンマ・改行・ダブルクォートが含まれているかどうかを判定してから、必要なときだけ囲む」というところです。毎回必ずダブルクォートで囲んでも CSV としては問題ありませんが、出力が少し読みにくくなるので、実務ではこのように条件付きで囲むことが多いです。
行全体を組み立てるユーティリティに発展させる
配列 → 1 行の CSV 文字列
先ほどの csv_escape_field() を使えば、配列から 1 行分の CSV を自前で組み立てることもできます。
/**
* 配列から 1 行分の CSV 文字列を生成する
*/
function array_to_csv_line(array $fields, string $delimiter = ','): string
{
$escaped = [];
foreach ($fields as $value) {
$escaped[] = csv_escape_field((string)$value);
}
return implode($delimiter, $escaped) . "\r\n";
}
PHP使い方の例です。
$row = [1, '山田, 太郎', '彼は"天才"です'];
echo array_to_csv_line($row);
// 出力: 1,"山田, 太郎","彼は""天才""です"
PHPファイルに書き込む場合は、これを fwrite() で順番に書いていけば、fputcsv() とほぼ同じことができます。
Excel 向け CSV で気をつけたいこと(軽く触れておく)
細かくやり始めるとキリがないのですが、実務で Excel 向け CSV を出すときに、よく出てくる話題が 2 つあります。
文字コードを Shift_JIS(CP932)にするか UTF-8 にするか。
先頭が = や + などの値を「数式」と誤認識されないようにするか。
これらは「CSV 用エスケープ」というより、「Excel 向けの追加対策」に近いので、ここでは深掘りしません。ただ、「Excel で開く前提なら、文字コードと先頭文字にも気を配る必要がある」と頭の片隅に置いておくと、後でハマりにくくなります。
まとめ:今日からの「CSV 用エスケープ」ユーティリティ
CSV 用エスケープの本質は、「1 セル分の文字列を、カンマや改行、ダブルクォートを含んでいても安全に CSV として扱える形にする」ことです。そのためにやるべきことは、次の 2 点に集約されます。
必要ならダブルクォートで囲む。
中のダブルクォートは "" に置き換える。
PHP では、まず fputcsv() を使うのが一番簡単で安全です。それでも「1 フィールドだけエスケープしたい」場面のために、次のようなユーティリティを持っておくと便利です。
function csv_escape_field(string $value): string
{
$needsQuote = strpbrk($value, ",\"\r\n") !== false;
if ($needsQuote) {
$escaped = str_replace('"', '""', $value);
return '"' . $escaped . '"';
}
return $value;
}
function array_to_csv_line(array $fields, string $delimiter = ','): string
{
$escaped = [];
foreach ($fields as $value) {
$escaped[] = csv_escape_field((string)$value);
}
return implode($delimiter, $escaped) . "\r\n";
}
PHPもし、あなたのコードが「implode(',', $row) でそのまま CSV を作っている」ような状態なら、そこがこのユーティリティに差し替えるべきポイントです。たった数行のエスケープを挟むだけで、「Excel で開いても壊れない CSV」に一気に近づきます。

