CSVをsplit(',')で分けると、「東京都,中央区」のように値の中へカンマが入ったデータまで別の列になります。行ごとの分割も先に行うと、引用符で囲まれたセル内の改行を、次のレコードと取り違えます。
GASではUtilities.parseCsvを使い、CSV文字列を表の形の2次元配列へ変換できます。ファイルへ書き込む前に、列数と内容を確認する段階を置くと、列ずれしたデータをそのままシートへ転記せずに済みます。
引用符を含む小さなCSVを用意する
例の列は「顧客ID」「備考」です。備考にはカンマ、改行、引用符を入れ、どこまでが1つの値かを確認します。CSV内で値の引用符を表すときは、引用符を2つ続ける書き方になります。
見た目の行数とデータの件数が同じとは限りません。セル内の改行があれば1件が複数行に見えるため、改行の数で取り込み件数を決めないようにします。文字コードも別の確認事項で、この例は既にJavaScriptの文字列になったデータを扱います。
公式のUtilitiesリファレンスでは、parseCsvが単一行または複数行のCSV文字列を2次元の文字列配列へ変換すると説明されています。タブ区切りなら、区切り文字を指定する別の呼び方を使います。
読み取って列数を確認するコード
次の例は見出しも含めて2列かを調べます。CSVの解析はUtilitiesへ任せ、その後の表の検査を別の関数に分けます。元の文字列にBOMがある場合は先頭の1文字だけを外します。
function requireCsvWidth(rows, width) {
if (!Array.isArray(rows) || rows.length === 0) throw new Error('CSVが空です');
rows.forEach(function (row, index) {
if (!Array.isArray(row) || row.length !== width) {
throw new Error('列数を確認してください: レコード' + (index + 1));
}
});
return rows;
}
function previewCsvImport() {
const csv = '\uFEFF顧客ID,備考\nA-01,"東京,中央"\nA-02,"1行目\n2行目"\nA-03,"""至急""です"';
const text = csv.replace(/^\uFEFF/, '');
const rows = requireCsvWidth(Utilities.parseCsv(text), 2);
console.log(JSON.stringify(rows));
return rows;
}
期待する配列は、見出しと3件のデータを合わせて4レコードです。A-01の備考は東京,中央の1セル、A-02は改行を含む1セル、A-03は"至急"ですという文字列になります。ログの\nは、JSONで表したセル内の改行です。
型の変換は取り込み後に決める
parseCsvの戻り値は文字列です。IDの0012をすぐ数値に変えると、先頭のゼロが消えます。金額、日付、IDを同じ変換に通さず、列ごとの意味に合わせて検査します。
見出しが予定と合っていることも、列数とは別に確認してください。2列なら何でもよいわけではなく、順番が入れ替わっていれば別の項目として読んでしまいます。見出しと期待する列名を照合してから、後続の集計へ進みます。
列数のエラーで表示するのは、改行で数えた行番号ではなく、解析後のレコード番号です。改行を含む値があるCSVでは両者が違うため、この区別をエラーメッセージにも残しています。
シートへ書く前に止める条件
取り込み先のセルへ保存する場合、イコールで始まる文字列を数式として扱うかどうかも確認が必要です。CSVを正しく解析できたことと、シートへ意図した型で保存できたことは別です。この例は配列の表示までで、シートへ書き込みません。
ローカルではUtilitiesの代用パーサーを使ったサンプルの受け渡しと、列数検査の正常・不一致・空配列を確認しました。GoogleのparseCsv実装そのものやファイルの文字コード判定は未実行です。実際のCSVでは小さなコピーを使い、引用符と改行を含む値が保たれるかを確かめてください。
逆に表からCSVを作る手順はCSVの書き出しで扱っています。シートへ渡す配列の形はスプレッドシートの読み書きも参照してください。
