2020年3月27日
VBAでCSVを読み込む
VBAでCSVを読み込む際、よく以下のようなコードを書くと思います。
1 2 3 4 5 6 7 8 9 10 11 | Sub CsvLoad() Dim lineBuf As String , csvCells As Variant Open "C:\Test.csv" For Input As #1 Do Until EOF(1) ’CSV行を読み込む Line Input #1, lineBuf ’CSVカラムをカンマ区切りで配列にする csvCells = Split(lineBuf, ",") Loop Close #1 End Sub |
このような書き方でもCSVは読み込めますが、以下の様なCSVデータが入ってきた場合に読み込む列がズレてしまいます。
“aaa”,”bbb”,“cc,ccc”
配列[1]=aaa
配列[2]=bbb
配列[3]=cc
配列[4]=ccc
ダブルクォートで囲われているので、意図する結果は以下です。
配列[1]=aaa
配列[2]=aaa
配列[3]=cc,ccc
「tmp = Split(buf, “,”)」で1行のデータをカンマで区切ってしまっている為ですが、これを解決するには「ダブルクォートが始まったら~」「ダブルクォートの終了文字数が~」等と条件が複雑になり、VBAで実現できなくはありませんが、非常に難解なコードになりメンテナンス性も悪くなります。
VBAでカンマの入ったデータを1つのカラムとして扱う尚且つコードを簡素にするには、VB.NETで採用されているTextFieldParserを使います。
そのままでは使えないので、ちょっと面倒ですがVB.NETやC#で一旦COMライブラリ(DLL)を作成し、ExcelのVBAで扱えるようにします。
サンプルコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | using Microsoft.VisualBasic.FileIO; using System; using System.Runtime.InteropServices; using System.Text; namespace VBACsvLibrary { /// <summary> /// インターフェイス /// </summary> [ComVisible( true ), InterfaceType(ComInterfaceType.InterfaceIsDual), Guid( "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" )] public interface IVBACsvLibrary { void FileLoad( string path, string enc = "shift_jis" , string delimiters = "," , int textFieldType = 0); bool EndOfData(); string [] ReadFields(); } /// <summary> /// TextFieldParserのVBA向けクラス /// </summary> [ClassInterface(ClassInterfaceType.None), ProgId( "VBACsvLibrary" ), Guid( "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" )] public class Parser : IVBACsvLibrary { // Microsoft.VisualBasic.FileIO.TextFieldParser TextFieldParser _parser = null ; /// <summary> /// 現在のカーソル位置とファイルの終端との間に、空行でもコメント行でもない行が存在しない場合、True を返します。 /// </summary> /// <returns>読み取るデータが他にない場合は True。それ以外の場合は False。</returns> public bool EndOfData() { return _parser.EndOfData; } /// <summary> /// CSVファイルを読み込みます。 /// </summary> /// <param name="path">ファイルパス</param> /// <param name="enc">ファイルの文字コードを指定。未指定の場合、shift_jis</param> /// <param name="delimiters">区切り記号を指定。未指定の場合、カンマ</param> /// <param name="textFieldType">ファイルが区切り形式か固定幅形式かを指定。 1 又は 未指定:区切り形式 | 0:固定幅形式</param> public void FileLoad( string path, string enc = "shift_jis" , string delimiters = "," , int textFieldType = 0) { _parser = new TextFieldParser(path, Encoding.GetEncoding(enc)); _parser.SetDelimiters(delimiters); _parser.TextFieldType = textFieldType == 0 ? FieldType.Delimited : FieldType.FixedWidth; } /// <summary> /// 現在行のすべてのフィールドを読み込んで文字列の配列として返し、次のデータが格納されている行にカーソルを進めます。 /// </summary> /// <returns>現在の行のフィールド値を格納する文字列の配列。</returns> public string [] ReadFields() { return _parser.ReadFields(); } } } |
※Guidは[ツール]-[GUID の作成]で発行する。
上記をDLLライブラリとして作成し、RegAsmで登録します。
“C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe” “C:\VBACsvLibrary.dll” /tlb /codebase
ライブラリを登録後、ExcelVBAの参照設定にDLLが表示されますのでチェックを入れます。
使い方サンプル
‘VBACsvLibrary
Dim TextFieldParser As VBACsvLibrary.Parser
Set TextFieldParser = New VBACsvLibrary.Parser
‘CSVファイル読み込み
TextFieldParser.Load (filePath)
‘CSVカラムを配列にして読み込む
Do Until TextFieldParser.EndOfData
csvData = TextFieldParser.ReadFields
Loop
VisualStudioが無くてもC#のコードをdllにコンパイルすることはできますが、Guidの発行はVS上で行わなければならないのでここでは紹介しません。