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上で行わなければならないのでここでは紹介しません。