今回はいよいよCSVを取り上げます。
下記のような売り上げデータのCSVファイルがあります。
No. | 日時 | 顧客名 | 顧客属性 | 来店種別 | 利用金額 |
---|---|---|---|---|---|
1 | 2020/10/4 | “住処商事” | xxxxxx | xxxx | 1300 |
2 | 2020/10/25 | “香料販売” | xxxxxx | xxxx | 14340 |
3 | 2020/10/25 | “Satoru co., Ltd” | xxxxxx | xxxx | 2340 |
… | …. | …. | …. | …. | …. |
CSVファイルの区分け記号(デリミタ)は一般的に”,”です。VBSでは特定の文字で、文字列を分割してくれるSplit関数があるので、1行をSplit関数を使って”,”で分割すれば良いように思えます。
しかし、上記データの”Satoru co., Ltd”のように、「一つのセルの中に”,”を含む」場合には、列がずれてしまいます。そこで、「文字列に”,”を含んでいても、ダブルクウォートでくくってあれば、一つのセルと解釈する。」関数を用意し、テンプレートに組み込みました。これを使えば、列のずれなくデータとして扱うことができます。
プログラムを紹介します。
const DQ = """" 'ダブルクォート const Delimita = "," const HeaderLine = 1 set fso = CreateObject("Scripting.FileSystemObject") set args = WScript.Arguments ParseCSV args(arg) WScript.Quit Sub ParseCSV( fileName ) DIM line, fileo, lineCount, schema, oneLine if fso.FileExists( filename ) then set fileo = fso.OpenTextFile(fileName , 1, false, false) set schema = CreateObject("Scripting.Dictionary") set oneLine = CreateObject("Scripting.Dictionary") linecount = 0 do until fileo.AtEndofStream line = fileo.ReadLine() if left( line, 1) <> "#" then linecount = linecount + 1 if linecount = HeaderLine then 'タイトル行 call ParseSchema( line, schema ) elseif linecount > HeaderLine then if ParseLine( line, oneLine ) = 1 then '解析有効 ' 処理したい内容をここに記述 WScript.echo Trim(oneLine.Item(schema.Item("顧客名"))) WScript.echo Trim(oneLine.Item(schema.Item("利用金額"))) end if end if end if loop fileo.close else WScript.echo "Can't file " & filename end if end sub Sub ParseSchema( line, schema ) dim columns, i line = Replace( line, DQ, "" ) schema.removeall columns = split(line, Delimita , -1) for i = 0 to Ubound(columns) if Trim(columns(i)) <> "" then schema.add Trim(columns(i)), i+1 end if next end sub Function ParseLine( line, rowData ) dim c, cp, qp, q, l, tmp rowData.RemoveAll() ParseLine = 0 c = 1 'column ID q = 0 'quote mode if left(line,1) = "#" then ParseLine = false exit function end if l = trim(line) do while len(l) > 0 if Left(l, 1) = DQ then q = 1 l = Mid( l, 2, len(l)-1) '左端の"をカット else q = 0 end if if q then qp = InStr(l, DQ) if qp = 0 then WScript.echo "フォーマットエラー:" & line exit function else tmp = left(l, qp -1 ) if len(trim(tmp)) = 0 then rowData.add c, "" else rowData.add c, tmp end if l = mid( l, qp + 1, len(l)- qp ) cp = InStr(l, ",") if cp = 0 then ParseLine = 1 exit function end if l = mid( l, cp + 1, len(l)- cp ) c = c + 1 end if else cp = InStr(l, ",") if cp = 0 then rowData.add c, trim(l) ParseLine = 1 exit function end if tmp = left(l, cp - 1) if len(trim(tmp)) = 0 then rowData.add c , "" else rowData.add c, tmp end if l = mid( l, cp + 1, len(l)- cp ) c = c + 1 end if loop ParseLine = 1 end Function
このスクリプトを
>cscript CSV-Read.vbs Sample.csv
のように呼び出すと
住友商事
1300
香料販売
14340
Satoru co., Ltd
2340
のように表示されるはずです。
ポイントは、”ParseSchema( line, schema)”という部分で、lineはヘッダーとなる行がはいっており、この関数を呼び出すとschemaディクショナリのキーが列名で、アイテムに列番号が保存されます。さらにデータ行はParseLine( line, oneLine)で、lineにはデータ行を渡し、この関数を呼び出すと、oneLineディクショナリにキーが列番号で、アイテムにデータが保存されます。
これによりoneLine.item(schema.item(列名))の戻り値が、その列のデータになります。プログラムでは列番号を直接指定しませんので、列が何列目かは関係ありません。プログラムを作成するうえで、何列目かを調べる必要はありませんし、データファイルの列の並びが変わってもプログラムを変更する必要はありません。
ここでParseSchemaはタイトル行を”,”で分割して解析をしています。タイトルの一部に”,”が来ることはないという想定です。しかし、データの文字列に”,”が含まれることはよくあるため、それを考慮して解析するのがParseLine関数です。関数の中身は解説しません。使用法だけわかっていれば十分と考えています。
この方法はかなり応用が広く、CSVの解析一般で利用可能です。是非自分で適当なCSVファイルについて、データを読み出してみてください。