何をしたいユーティリティか:「TSV 生成」
ここで目指すのは、業務データ(配列や配列の配列)から「TSV 文字列」を安全に生成するユーティリティです。
TSV(Tab Separated Values)は「タブ区切りテキスト」で、
Excel やスプレッドシートに貼り付けやすく、CSV よりもシンプルなルールで扱えるのが特徴です。
toTsvRow([1, "山田太郎", "営業部"]);
// "1\t山田太郎\t営業部"
toTsv([
[1, "山田太郎", "営業部"],
[2, "佐藤花子", "開発部"],
]);
// "1\t山田太郎\t営業部\n2\t佐藤花子\t開発部"
JavaScriptこういう「TSV 文字列」を、毎回手書きの連結ではなく、
ユーティリティ関数に任せるのがゴールです。
TSV のルールをざっくり押さえる
区切り文字は「タブ」
CSV はカンマ , で区切りますが、TSV はタブ \t で区切ります。
1 行は「タブ区切りのフィールドの並び」で、
行と行は改行 \n で区切ります。
1\t山田太郎\t営業部\n2\t佐藤花子\t開発部
という 1 本の文字列が、2 行の TSV になります。
CSV よりエスケープがシンプル
CSV はカンマ・改行・ダブルクォートの扱いがややこしいですが、
TSV は「タブと改行だけ気をつければよい」と考えると分かりやすいです。
ただし、タブや改行がフィールドの中に入ると、列や行がずれるので、
そこはちゃんと処理する必要があります。
まずは 1 セル分の「TSV エスケープ」を考える
どんな値をどう扱うか
TSV の 1 セルに入れるとき、最低限決めておきたいルールはこれです。
null/undefinedは空文字にする- 数値や日付は文字列化する
- タブ
\tはスペースに置き換える(列ズレ防止) - 改行はそのまま残すか、スペースにするかを決める
ここでは、「タブはスペースに置き換え」「改行はそのまま(Excel でセル内改行として見える)」という方針でいきます。
tsvEscape 関数を作る
実装コード
function tsvEscape(value) {
if (value == null) {
return "";
}
let str = String(value);
str = str.replace(/\t/g, " ");
return str;
}
JavaScript重要なポイントをかみ砕いて説明する
null / undefined は空セルとして扱います。
if (value == null) {
return "";
}
JavaScriptTSV では、空セルを表現したいことがよくあるので、
ここでは「何もない値は空文字」として扱っています。
次に、文字列化します。
let str = String(value);
JavaScript数値でも日付でも、TSV に出すときは最終的に文字列です。
ここで一度文字列にしてしまえば、あとは文字列として処理できます。
そして、タブをスペースに置き換えます。
str = str.replace(/\t/g, " ");
JavaScriptフィールドの中にタブがあると、「列の区切り」と区別がつかなくなり、
Excel などで開いたときに列がずれます。
なので、「フィールド内のタブは禁止」と割り切って、
スペースに置き換えるのが安全です。
改行(\n や \r\n)はここではそのまま残しています。
Excel で開くと「セル内改行」として表示されます。
配列から 1 行の TSV を作る
toTsvRow 関数
1 セル分のエスケープができたので、
次は「配列 → 1 行の TSV 文字列」を作る関数です。
function toTsvRow(values) {
return values.map(tsvEscape).join("\t");
}
JavaScriptやっていることはシンプルで、
- 各要素を
tsvEscapeで安全な文字列にする - タブ
\tで join する
これで 1 行分の TSV ができます。
動作例
toTsvRow([1, "山田太郎", "営業部"]);
// "1\t山田太郎\t営業部"
toTsvRow([2, "佐藤\t花子", "開発部"]);
// "2\t佐藤 花子\t開発部"(タブがスペースに変わる)
toTsvRow([3, "A\nB", "総務部"]);
// "3\tA\nB\t総務部"
JavaScript2 行目の "佐藤\t花子" は、
フィールド内のタブがスペースに変わっているのがポイントです。
2 次元配列から「TSV 全体」を作る
toTsv 関数
最後に、「行の配列 → TSV 全体の文字列」を作る関数です。
function toTsv(rows) {
return rows.map(toTsvRow).join("\n");
}
JavaScriptここでもやっていることはシンプルで、
- 各行(配列)を
toTsvRowで 1 行の文字列にする - 行と行を改行
\nで join する
これで、TSV ファイルとしてそのまま保存できる 1 本の文字列ができます。
動作例
const rows = [
[1, "山田太郎", "営業部"],
[2, "佐藤花子", "開発部"],
[3, "A\nB", "総務部"],
];
const tsv = toTsv(rows);
/*
"1\t山田太郎\t営業部\n2\t佐藤花子\t開発部\n3\tA\nB\t総務部"
*/
JavaScriptこの文字列を .tsv ファイルとして保存し、
Excel で開くと、3 行 3 列の表として表示されます。
3 行目の「A\nB」は、1 セルの中で改行されて見えます。
実務で意識してほしい設計のポイント
「TSV 生成は必ずユーティリティを通す」
文字列連結で
id + "\t" + name + "\t" + dept
JavaScriptのように書き始めると、
タブや改行が混ざった瞬間に壊れます。
「TSV に出すときは必ず tsvEscape / toTsvRow / toTsv を通す」
というルールにしておくと、列ズレ事故をかなり防げます。
タブと改行の扱いをチームで決める
ここでは「タブはスペースに置き換え」「改行は許容」という方針にしましたが、
業務によっては「改行もスペースにしたい」「改行を \\n にしたい」などの要件もあります。
例えば、改行もスペースにしたいなら、tsvEscape をこう変えられます。
function tsvEscape(value) {
if (value == null) {
return "";
}
let str = String(value);
str = str.replace(/\t/g, " ");
str = str.replace(/\r?\n/g, " ");
return str;
}
JavaScript「どこまでを許容して、どこからを潰すか」は仕様なので、
ユーティリティの中で一貫したルールにしておくのが大事です。
ヘッダ行も一緒に生成する
TSV を業務で使うときは、ヘッダ行(列名)を付けることが多いです。
const header = ["ID", "氏名", "部署"];
const body = [
[1, "山田太郎", "営業部"],
[2, "佐藤花子", "開発部"],
];
const tsv = toTsv([header, ...body]);
JavaScriptこうしておけば、Excel で開いたときに列名が分かりやすくなります。
少し手を動かして感覚をつかむ
コンソールで、次のようなコードを実際に打ってみてください。
tsvEscape("山田太郎");
tsvEscape("佐藤\t花子");
tsvEscape("A\nB");
toTsvRow([1, "山田太郎", "営業部"]);
toTsvRow([2, "佐藤\t花子", "開発部"]);
const rows = [
["ID", "氏名", "部署"],
[1, "山田太郎", "営業部"],
[2, "佐藤\t花子", "開発部"],
];
const tsv = toTsv(rows);
JavaScripttsv の中身をコピーして .tsv ファイルに貼り付け、
Excel で開いてみてください。
列がずれず、タブがちゃんと区切りとして機能していること、
フィールド内のタブがスペースに変わっていることが確認できるはずです。
そのうえで、自分のプロジェクトに
export function tsvEscape(...) { ... }
export function toTsvRow(...) { ... }
export function toTsv(...) { ... }
JavaScriptを置き、
「TSV を出したくなったら必ずここを通す」
というルールを作ってみてください。
それだけで、あなたの TSV 出力は、場当たり的な文字列連結から、
業務で安心して使える「堅牢な TSV 生成ユーティリティ」に変わっていきます。
