Engineer Blog開発者ブログ

2020.3.27

VBAでCSVを読み込む

Icons made by Freepik from www.flaticon.com

 

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

Contact
お問い合わせ

電話番号0120 - 889 - 236
受付時間:平日 9:00-18:00