🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Q&A

解決済

1回答

2643閲覧

GASの正規表現でドライブのCSVデータを1行ずつ切り分けたい

kktok

総合スコア23

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

0グッド

0クリップ

投稿2021/03/12 07:34

前提

ドライブに入っているCSVデータを行ごとに切り分け、処理を行いたいと考えていました。

parseCsvメソッドを用いて配列に変換しようとしました。

GAS

1var data = file.getBlob().getDataAsString("Shift_JIS"); 2var csv = Utilities.parseCsv(data); 3

この分では不明なエラーが起こり処理を続けられないため
改行文字(\r\n)を行の区切りとして判定させようと考えました。

GAS

1 var data = file.getBlob().getDataAsString('Shift-JIS'); 2 var splitDataArr = data.split(/\r\n/gm);

この正規表現では一部の項目についてはうまくいきましたが
1セルの文章内で改行文字を使用している項目があった場合正常な切り分け処理ができません。

試してみたこと

GAS

1var splitDataArr = data.split(/"\r\n/gm);

のように、文章の区切りのダブルクォーテーション改行と指定したところ、
以前よりは正確に区切ることが出来ましたが文字列の最後がダブルクォーテーションかつ改行文字を使っている箇所では
やはり行の区切りと同じように判定されてしまいました。

より正確な正規表現をお教えいただけますと幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

正規表現 (Regex) を利用してCSVファイルやTSVファイルを読み込む - C#プログラミング
https://www.ipentec.com/document/csharp-read-csv-file-by-regex
↑を参考にさせてもらいました。

js

1csvCRLF.split(/\r\n(?=(?:[^"]*"[^"]*")*[^"]*$)/g).slice(0,-1)

ただこのコードより、動作が保証された偉い人が作ったライブラリで解析するべきだと思います。


※以下の解説などは、自分の趣味の覚書みたいなものなので、読み飛ばしてもらって構いません。

csvでは、"これの出現回数が奇数の時は囲まれてる文字列なので、これを数え偶数の時だけマッチさせる必要があります。
走査の流れ的に(?<=)後読みすべきなんですが、後読みは繰り返し記号が使えない仕様。
なので方向を変えて、csv全文は偶数and後ろが偶数時は、先も偶数、という関係から(?=*$)先読みでファイル終わりまでの"を毎回全部数えます。全文なので$が改行で止まるmフラグは付けれません。

しかしこれは効率が悪く、エラーがあっても対応し辛いです。あと正規表現読んでると脳が疲れます。
なので前から数えるわかり易い方法で書いてみました。

js

1/** csvを2次元配列に変換する。 2 * LFのcsv用、最後改行必須。バグのチェック必須。 3 * 要素の中に改行あっても良い。ダブルクウォートの囲いは無しもあり。 4 * 「"」の閉じ忘れはそのまま閉じてないことと解釈。 5 * @param {string} csv - 6 * @return {Array} 7 */ 8const csvParseKun = (csv) => { 9 let re = /[,"\n]/g 10 // console.log([csv]) 11 let parsed = [[]]; 12 let key = 0 13 let guuSuu = true 14 let beginIndex = 0 15 let result 16 while (result = re.exec(csv)) { 17 if (result[0] === '"') { guuSuu = !guuSuu; continue } 18 if (guuSuu === false) continue 19 //”を適切に消し残す。 20 let str = csv.slice(beginIndex, result.index) 21 if (str[0] === '"') { 22 str = str.slice(1, -1).replace(/""/g, '"') 23 } 24 parsed[key].push(str) 25 //最後まで来たら終わり 26 if (re.lastIndex === csv.length) break 27 //次の準備 28 beginIndex = re.lastIndex 29 if (result[0] === '\n') { key += 1 ;parsed[key]=[]} 30 } 31 return parsed 32} 33 34//test用 35let csv = `0,"a",""",,","1改行\n","2改行\n\n","""""","末"\n"二行目"\n3,\n` 36//CRLFならLFに変換 37// let csv = csvCRLF.replace(/\r\n/g, '\n') 38console.log(csvParseKun(csv))

投稿2021/03/13 01:32

gakuburu

総合スコア125

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kktok

2021/03/15 02:07

ご回答いただきありがとうございました。この質問以前にparseCsvメソッドの不良を疑って投稿した質問にいただいたほか回答と合わせて大変勉強になりました。問題も解決し、無事取り込むことが出来ました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問