2020年3月27日
VBAでCSVを読み込む
VBAでCSVを読み込む際、よく以下のようなコードを書くと思います。
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で扱えるようにします。
サンプルコード
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上で行わなければならないのでここでは紹介しません。