JavaScript Tips | 文字列ユーティリティ:業務用 - CSV エスケープ

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「CSV エスケープ」

ここで作りたいのは、「任意の値を“CSV の 1 セルとして安全な文字列”に変換する関数」です。

CSV はただのカンマ区切りではなく、
「カンマ」「改行」「ダブルクォート」が入っているときに、正しくエスケープしないと壊れます

だから、業務では

csvEscape("山田太郎");          // そのまま "山田太郎"
csvEscape("1,234");            // "\"1,234\""
csvEscape("A\nB");             // "\"A\nB\""
csvEscape("He said \"Hi\"");   // "\"He said \"\"Hi\"\"\""
JavaScript

のように、「CSV として安全な形」に変換するユーティリティが必須になります。


CSV のルールをざっくり理解する

どんなときにダブルクォートで囲む必要があるか

CSV の基本ルール(RFC4180 ベースでざっくり)を、必要なところだけ押さえます。

次のどれかが含まれているフィールドは、ダブルクォートで囲む必要があります

  • カンマ ,
  • 改行(\n\r\n
  • ダブルクォート "

逆に言うと、何も特殊文字がなければ、そのまま出してよいです。

ダブルクォートはどうエスケープするか

フィールド内にダブルクォート " がある場合、
CSV では ""(2 つ連続)に置き換える ルールになっています。

例:

元の文字列:He said "Hi"
CSV フィールド内:"He said ""Hi"""

つまり、

  1. 文字列中の """ に置き換える
  2. フィールド全体を " で囲む

という二段構えです。


シンプルな csvEscape 関数を作る

実装コード

function csvEscape(value) {
  if (value == null) {
    return "";
  }

  const str = String(value);

  const needsQuote =
    str.includes(",") ||
    str.includes("\n") ||
    str.includes("\r") ||
    str.includes('"');

  if (!needsQuote) {
    return str;
  }

  const escaped = str.replace(/"/g, '""');

  return `"${escaped}"`;
}
JavaScript

重要なポイントをかみ砕いて説明する

null / undefined の扱い

if (value == null) {
  return "";
}
JavaScript

CSV では、空セルを表現したいことがよくあります。
ここでは nullundefined を「空文字」として扱い、
a,,c のように「何もないセル」として出力できるようにしています。

まずは文字列に変換する

const str = String(value);
JavaScript

数値でも日付でも、CSV に出すときは最終的に文字列です。
ここで一度文字列にしてしまえば、あとは「文字列としてのルール」だけを考えればよくなります。

「囲む必要があるか」を判定する

const needsQuote =
  str.includes(",") ||
  str.includes("\n") ||
  str.includes("\r") ||
  str.includes('"');
JavaScript

カンマ・改行・ダブルクォートのどれかが含まれていれば、
そのフィールドはダブルクォートで囲む必要があります。

ここで大事なのは、「何でもかんでも囲う必要はない」ということです。
シンプルな値("123", "山田太郎" など)は、そのまま出した方が読みやすいことも多いです。

ダブルクォートを "" に置き換える

const escaped = str.replace(/"/g, '""');
JavaScript

ここが CSV エスケープの核心です。

""" に置き換えることで、
フィールド内のダブルクォートを「文字としての "」として扱えるようにします。

最後に全体を " で囲む

return `"${escaped}"`;
JavaScript

これで、CSV として安全な 1 フィールドが完成します。


実際の動きを例で確認する

特殊文字なし

csvEscape("山田太郎");   // "山田太郎"
csvEscape(123);         // "123"
csvEscape("ABC123");    // "ABC123"
JavaScript

カンマ・改行・ダブルクォートがないので、そのまま返っています。

カンマを含む場合

csvEscape("1,234");     // "\"1,234\""
csvEscape("A,B,C");     // "\"A,B,C\""
JavaScript

カンマがあるので、ダブルクォートで囲まれます。

実際の CSV 行としては、例えば

ID,Name,Amount
1,山田太郎,"1,234"

のような形になります。

改行を含む場合

csvEscape("A\nB");      // "\"A\nB\""
csvEscape("行1\r\n行2"); // "\"行1\r\n行2\""
JavaScript

改行があるので、ダブルクォートで囲まれます。
Excel などで開くと、1 セルの中に改行として表示されます。

ダブルクォートを含む場合

csvEscape('He said "Hi"');
// "\"He said \"\"Hi\"\"\""
JavaScript

中身だけ見ると、

元の文字列:He said "Hi"
エスケープ後:"He said ""Hi"""

となっています。


行全体を CSV にするユーティリティ

配列を 1 行の CSV にする

csvEscape が 1 セル分を担当するので、
行全体は「配列をエスケープしてカンマで join」すれば作れます。

function toCsvRow(values) {
  return values.map(csvEscape).join(",");
}
JavaScript

使い方はこんな感じです。

const row = toCsvRow([
  1,
  "山田太郎",
  "1,234",
  'He said "Hi"',
  "A\nB",
]);

// 結果(見やすく改行)
// 1,山田太郎,"1,234","He said ""Hi""","A
// B"
JavaScript

これをそのままファイルに書き出せば、
Excel などで正しく 1 行として読み込めます。


実務で意識してほしい設計のポイント

一つ目は、「CSV エスケープは絶対に自前で雑に書かない」ということです。
value.replace(",", "\\,") のような「なんちゃってエスケープ」は、
改行やダブルクォートを扱えず、すぐに壊れます。

「CSV に出すときは必ず csvEscape を通す」
「行を作るときは必ず toCsvRow を通す」

というルールにしておくと、事故が激減します。

二つ目は、「null や undefined の扱いをチームで決める」ことです。
空セルにするのか、"0" にするのか、"N/A" にするのか——ここは業務仕様です。
ユーティリティの中で一貫したルールにしておけば、画面ごとに挙動が変わることを防げます。

三つ目は、「入力側(CSV を読む側)のルールもセットで考える」ことです。
今回は「書き出し」の話をしましたが、読み込み側も
「ダブルクォートで囲まれたフィールド」「"" の扱い」などを正しく実装する必要があります。
書き出しと読み込みのルールが揃っていると、往復しても壊れない CSV になります。


少し手を動かして感覚をつかむ

コンソールで、次のようなコードを実際に打ってみてください。

csvEscape("山田太郎");
csvEscape("1,234");
csvEscape("A\nB");
csvEscape('He said "Hi"');

toCsvRow([1, "山田太郎", "1,234", 'He said "Hi"', "A\nB"]);
JavaScript

返ってきた文字列をコピーして、
実際に .csv ファイルに貼り付けて保存し、Excel で開いてみてください。

「セルが崩れない」「改行やダブルクォートがちゃんと見える」
という体験ができたら、それが “正しい CSV エスケープができている” という証拠です。

そのうえで、自分のプロジェクトに

export function csvEscape(...) { ... }
export function toCsvRow(...) { ... }
JavaScript

を置き、

「CSV に出すときは必ずここを通す」

というルールを作ってみてください。
それだけで、あなたの CSV 出力は、場当たり的な文字列連結から、
業務で安心して使える「堅牢な CSV エスケープユーティリティ」に変わっていきます。

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