スプレッドシートをCSVへ書き出すときは、カンマで値をつなぐだけでは足りません。商品名にカンマがある、備考に改行がある、引用符が入っている。こうした値を1つの項目として残せる形にします。
ここでは、表示されている文字をCSVへ変換し、保存前に件数を確認する例を示します。実際のシートやDriveには書き込んでいません。値の取得方法はGoogle公式のRangeリファレンスで確認しています。
数値か表示文字かを選ぶ
getValuesは数値や日付などセルの値を返します。getDisplayValuesは、通貨記号や日付の表示書式を反映した文字列を返します。どちらを使うかで、CSVへ入る内容が変わります。
担当者が画面で見た文字を受け渡したいなら表示文字が候補になります。一方、別のシステムで金額を計算する用途では、桁区切りや通貨記号を含む文字が適切かを確認します。商品コードの先頭ゼロを残す条件も、取り込み先と合わせて決めます。
CSVには、シートの表示書式や値の型をそのまま保存する仕組みはありません。書き出した文字が受け取る側でどう解釈されるかまで確認します。
カンマと引用符を扱う関数
次の関数は各項目を引用符で囲み、値の中の引用符を二重にします。行の区切りはCRLFです。シートへの接続を含まないため、架空の表を使って変換だけを確認できます。
function csvCell(value) {
const text = value === null || value === undefined ? '' : String(value);
return '"' + text.replace(/"/g, '""') + '"';
}
function rowsToCsv(rows) {
return rows.map(function (row) {
return row.map(csvCell).join(',');
}).join('\r\n') + '\r\n';
}
たとえば商品名がA,限定版でも、カンマで2項目に分かれない形にします。備考に改行がある場合も、その改行は項目の中に残ります。「行数を改行の数だけで数える」点検では、CSVのレコード数と一致しない場合があります。
シートを読み、保存を分ける
次は練習用シートを読む例です。SAVEはfalseなので、既定では保存しません。保存する段階では作成先とファイル名を確認し、Driveで生成された内容を開いて確認します。
function previewOrSaveCsv() {
const SAVE = false;
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('練習');
if (!sheet) throw new Error('練習シートが見つかりません');
const rows = sheet.getDataRange().getDisplayValues();
const csv = rowsToCsv(rows);
console.log('出力予定の行数: ' + rows.length);
if (!SAVE) return;
const file = DriveApp.createFile('練習用.csv', csv, MimeType.CSV);
console.log('作成したファイルID: ' + file.getId());
}
ファイルの作成方法はDriveAppの公式説明を確認してください。この例では、同じ名前の既存ファイルを書き換える処理は入れていません。再実行したときにファイルが増える運用でよいかも、保存前に決めます。
出力結果で見る項目
確認用の表には通常の文字、カンマ、引用符、改行、空欄、先頭ゼロを持つコードを入れます。CSVを取り込み先で開き、列数、見出し、件数、金額、コードの文字が保たれるかを照合します。
文字化けした場合は、元の文字、保存されたファイル、開く側の文字コード指定を分けて確認します。開けたから正しいと判断せず、合計や先頭ゼロも元の表と比べます。
また、引用符で囲む処理は取り込み先での数式実行を防ぐための処理ではありません。外部入力を含むCSVを配布する場合は、先頭の記号などをどう扱うか、取り込み先の条件を確認します。OWASPのCSV Injection解説も確認先になります。
最初は小さな表で、元の表示とCSVの項目が対応するかを見ます。件数を増やす段階でも、元のシート名、範囲、出力日時、保存したファイルIDを残しておくと、どの表から作ったCSVかを追いやすくなります。
入力の不備を確かめる工程は取得データの欠損検知、GASの処理を分ける方法はGASコードの分割で扱っています。
