作成日 2011/1/10
最終更新日 2011/1/10

CSVファイルを読み込む方法

 CSVファイルを読み込む際、その仕様をちゃんと理解する必要があります
 例えば、データ内にカンマが入っているときは、データはダブルクォートで囲まれているのですが、そういったことを考慮する必要があります。
 また、上記の方法ではなく\でエスケープする("\,"とする)場合もあり、この場合の対処も考える必要があります。

1.CSVファイルとは
2.簡易的な解析方法
3.データがダブルクォートで囲まれる場合
4.データが\でエスケープされる場合
5.サンプルのダウンロードと実行
6.参考資料など

1.CSVファイルとは

 まずは、CSVファイルとはどのようなものなのかを、説明します。(※説明内容はこのページに関係のある内容のみですから、ちゃんとした仕様は自分で調べてください。)
 CSVはComma-Separated Valuesの略で、「カンマ区切り」のことです。
 この形式は表形式のデータを、テキストファイル内にカンマで区切ったデータを羅列するといった形式で、仕様が簡単なため、良く使われます。
 データ例(ファイル内のデータの例)としては、以下のようになります。

aaa,bbb
123,456
図1 CSVファイルの例

 さて、問題はここからです。  データの中にカンマが入っていたらどうするか?その場合、データをダブルクォートでくくります。改行がある場合も同じです。データ内にダブルクォートがある場合はそれを2つにします(図2)。
"aa,a","bb
b"
"12""3",456
図2 データをダブルクォートでくくる場合のCSVファイルの例(「aa,a」、「bb
b」、「12"3」というデータがある場合)

 ただ、図2のようにする場合もあるのですが(Excelはそうです)、MySQLなどで\でエスケープするものがあるので考慮が必要となる場合があります(図3)。
aa\,a,bb\
b
12\\3,456
図3 \でエスケープする場合のCSVファイルの例(「aa,a」、「bb
b」、「12\3」というデータがある場合)


このページのトップへ

2.簡易的な解析方法

 データにカンマ、改行、ダブルクォートそれからエスケープ文字(\)が無い場合は、VBA標準のSplit関数でいいでしょう。
Split関数はVBAのバージョンが5の場合は使用できません。その場合自作する必要がありますが、このページからダウンロードできるサンプルでは、Split関数を自作しているのでVBAのバージョンが5でも動作します。
 Split関数の戻りの配列の開始番号は0からですから、そこだけ注意が必要です。
 もちろん、後で説明する方法でやっても良いのですが、簡単(バグが発生しにくい)のと、処理が高速なのが良いです。
 とりあえず、サンプルソースです(注意:サンプルなのでエラー処理などはやっていません。またセル操作での高速化も行っていません)。

サンプルソース
Module1(標準モジュール)
''
' CSVファイル読込みサンプル
'
' このサンプルは、CSV内のデータにカンマ、ダブルクォート、エスケープ文字、改行が
' 無い場合に適用できます。
'
' @author 宮崎 崇 http://darumaexcel.uijin.com/index.html
' @date 2011年1月9日

Option Explicit

''
' CSVファイルを読み込み、アクティブシートに出力します
'
' @param filePath CSVファイルのパス
'

Public Sub readCSV_simple(ByVal filePath As String)

    Dim outputSheet As Worksheet    ' 出力先シート
    Dim outputRow As Long           ' 出力先の行番号
    Dim outputCol As Long           ' 出力先の列番号
    Dim fileNo As Integer           ' ファイル番号
    Dim tmpString As String         ' ファイルから読み込んだ文字列の一時格納場所
#If VBA6 Then
    Dim spString() As String        ' カンマで区切った文字列
#Else
    Dim spString As Variant
#End If

    ' 出力先初期化
    Set outputSheet = ActiveSheet
    outputRow = 1
    outputCol = 1

    ' ファイルを開く
    fileNo = FreeFile
    Open filePath For Input Access Read As #fileNo

    ' ファイルを読み込み、シートに出力
    Do While Not EOF(fileNo)
        Line Input #fileNo, tmpString
        ' カンマで区切る
        spString = Split(tmpString, ",")
        'シートに出力
        For outputCol = 1 To UBound(spString) + 1
            outputSheet.Cells(outputRow, outputCol).Value = spString(outputCol - 1)
        Next

        outputRow = outputRow + 1
    Loop

    ' ファイルを閉じる
    Close #fileNo

End Sub


このページのトップへ

3.データがダブルクォートで囲まれる場合

 以下の3つを説明します。
 (1)VBAを使って読む(データに改行がない場合)
 (2)VBAを使って読む(データに改行がある場合)
 (3)エクセルを使って読み込む


(1)VBAを使って読む(データに改行がない場合)
 "bb
b"のようにデータ内に改行が含まれるデータが無い場合について説明します。
 「(2)VBAを使って読む(データに改行がある場合)」のやり方でも良いのですが、こちらの方が簡単(バグが発生しにくい)のと、処理が高速でメモリの使用量も少ないと思います。

 やり方ですが、一行データを1文字づつ見ていき、カンマがあった場合、ダブルクォートの出現回数が2の倍数のときに文字の切り分け処理を行うようにします。

 とりあえず、サンプルソースです(注意:サンプルなのでエラー処理などはやっていません。またセル操作での高速化も行っていません)。

サンプルソース
Module2(標準モジュール)
''
' CSVファイル読込みサンプル
'
' このサンプルはデータ内にカンマ、ダブルクォートがあっても処理できるようにしてあります。
' ※データ内の改行は正しく認識されません
'
' @author 宮崎 崇 http://darumaexcel.uijin.com/index.html
' @date 2011年1月9日

Option Explicit

''
' CSVファイルを読み込み、アクティブシートに出力します
'
' @param filePath CSVファイルのパス
'

Public Sub readCSV_dblQuoteWithoutCRLF(ByVal filePath As String)

    Dim outputSheet As Worksheet    ' 出力先シート
    Dim outputRow As Long           ' 出力先の行番号
    Dim outputCol As Long           ' 出力先の列番号
    Dim fileNo As Integer           ' ファイル番号
    Dim tmpString As String         ' ファイルから読み込んだ文字列の一時格納場所
#If VBA6 Then
    Dim spString() As String        ' カンマで区切った文字列
#Else
    Dim spString As Variant
#End If

    ' 出力先初期化
    Set outputSheet = ActiveSheet
    outputRow = 1
    outputCol = 1

    ' ファイルを開く
    fileNo = FreeFile
    Open filePath For Input Access Read As #fileNo

    ' ファイルを読み込み、シートに出力
    Do While Not EOF(fileNo)
        Line Input #fileNo, tmpString
        ' カンマで区切る
        spString = Split_withDblQuote(tmpString)
        'シートに出力
        For outputCol = 1 To UBound(spString) + 1
            outputSheet.Cells(outputRow, outputCol).Value = spString(outputCol - 1)
        Next

        outputRow = outputRow + 1
    Loop

    ' ファイルを閉じる
    Close #fileNo

End Sub

''
' 文字をカンマで区切ります。
'
' 結果は文字列の一次元配列です。
' 配列の最初の要素番号(LBound)は0です。
' 引数lineStringが空文字の場合はUBoundが-1(LBoundは0)の配列を返します。
'
' 例:
' <pre>
' aaa,"bb,b","c""cc"
'  ↓
' aaa
' bb,b
' c"cc
' </pre>
'
' @param lineString 1行のデータ
' @return 区切った後の文字列(戻り値は定義上はVariant型ですが、実際にはString()を返します)
'

Private Function Split_withDblQuote(ByVal lineString As String) As Variant

    Const delimiter As String = ","         ' 区切り文字
    Const quotationMarks As String = """"   ' 引用符文字
    Dim strLen As Long                      ' 引数の文字列長
    Dim quotationMarksCount As Long         ' 引用符文字の出現回数
    Dim spStrings1 As Collection            ' 区切った直後の文字列
    Dim spStrings2 As Collection            ' 区切った後、引用符を修正したもの
    Dim i As Long                           ' ループカウンタ
    Dim oldCol As Long                      ' 1つ前の区切り文字の位置
    Dim tmpString As String                 ' 一時領域
    Dim strings() As String                 ' 戻り値

    ' 初期化
    strLen = Len(lineString)
    quotationMarksCount = 0
    Set spStrings1 = New Collection
    Set spStrings2 = New Collection
    oldCol = 0

    ' 空文字の場合
    If strLen = 0 Then
        Split_withDblQuote = Split("", delimiter)
        Exit Function
    End If

    ' 区切り文字で分割する
    For i = 1 To strLen
        tmpString = Mid$(lineString, i, 1)
        If tmpString = quotationMarks Then
            quotationMarksCount = quotationMarksCount + 1
        ElseIf tmpString = delimiter Then
            If quotationMarksCount Mod 2 = 0 Then
                Call spStrings1.Add(Mid$(lineString, oldCol + 1, i - oldCol - 1))
                oldCol = i
            End If
        End If
        If i = strLen Then
            Call spStrings1.Add(Mid$(lineString, oldCol + 1, i - oldCol))
        End If
    Next

    ' 最初と最後の引用符を取り除き、
    ' 引用符が連続している箇所を1つにする
    For i = 1 To spStrings1.Count
        tmpString = spStrings1.Item(i)
        If Left$(tmpString, 1) = quotationMarks And _
            Right$(tmpString, 1) = quotationMarks Then

            tmpString = Mid$(tmpString, 2, Len(tmpString) - 2)

            Call spStrings2.Add(Replace$(tmpString, quotationMarks & quotationMarks, quotationMarks))
        Else
            Call spStrings2.Add(tmpString)
        End If
    Next

    ' 一次元配列にして返す
    ReDim strings(0 To spStrings2.Count - 1)
    For i = 1 To spStrings2.Count
        strings(i - 1) = spStrings2.Item(i)
    Next

    Split_withDblQuote = strings

End Function

(2)VBAを使って読む(データに改行がある場合)
 さっそく、サンプルソースです(注意:サンプルなのでエラー処理などはやっていません。またセル操作での高速化も行っていません)。さっきのソースと比べ、長い・・・。

サンプルソース
Module3(標準モジュール)
''
' CSVファイル読込みサンプル
'
' このサンプルはデータ内にカンマ、ダブルクォート、改行があっても処理できるようにしてあります。
' ※ソースの見易さはデータ内に改行無い場合とした方が上ですし、処理速度、メモリ使用量も
' データ内に改行無い場合とした方が少なくて済みます。
'
' @author 宮崎 崇 http://darumaexcel.uijin.com/index.html
' @date 2011年1月9日

Option Explicit

''
' CSVファイルを読み込み、アクティブシートに出力します
'
' @param filePath CSVファイルのパス
'

Public Sub readCSV_dblQuoteWithCRLF(ByVal filePath As String)

    Dim outputSheet As Worksheet        ' 出力先シート
    Dim outputRow As Long               ' 出力先の行番号
    Dim outputCol As Long               ' 出力先の列番号
    Dim fileNo As Integer               ' ファイル番号
    Dim tmpString As String             ' ファイルから読み込んだ文字列の一時格納場所
    Dim lineStrings As Collection       ' 1行データの配列
    Dim spStringString As Collection    ' カンマで区切った後の文字列
#If VBA6 Then
    Dim spString() As String
#Else
    Dim spString As Variant
#End If

    ' 出力先初期化
    Set outputSheet = ActiveSheet

    ' 一行データの配列初期化
    Set lineStrings = New Collection

    ' ファイルを開く
    fileNo = FreeFile
    Open filePath For Input Access Read As #fileNo

    ' ファイルを読み込み
    Do While Not EOF(fileNo)
        Line Input #fileNo, tmpString
        Call lineStrings.Add(tmpString)
    Loop
    ' ファイルを閉じる
    Close #fileNo

    ' カンマで区切る
    Set spStringString = Split_dblQuote(lineStrings)

    ' シートに出力する
    For outputRow = 1 To spStringString.Count
        spString = spStringString.Item(outputRow)
        For outputCol = 1 To UBound(spString) + 1
            outputSheet.Cells(outputRow, outputCol).Value = spString(outputCol - 1)
        Next
    Next

End Sub

''
' 1行データの配列をカンマで区切ります。
'
' @param lineStrings 1行データの配列(Collectionの中はString)
' @return カンマで区切った後の文字列の配列。Collection内のStringの配列の要素の開始番号は0からです。
'        strCollectionの中で空行(strCollection.Item(i).Count = 0の場合)の
'        Stringの配列の要素の終了番号は-1(この場合でも開始番号は0)となります。

Private Function Split_dblQuote(ByVal lineStrings As Collection) As Collection

    Const delimiter As String = ","             ' 区切り文字
    Const quotationMarks As String = """"       ' 引用符文字
    Const newLineMarks As String = vbNewLine    ' 改行コード
    Dim quotationMarksCount As Long             ' 引用符文字の出現回数
    Dim spStringsStrings1 As Collection         ' 区切った後文字列。中身はCollection
    Dim spStrings1 As Collection                '

    Dim spStringsStrings2 As Collection         ' 区切った後文字列。中身はCollection
    Dim spStrings2 As Collection                '

    Dim lineStr As String                       ' 1行データ
    Dim lineStrLen As Long                      ' 1行データの文字列長

    Dim tmpStr As String

    Dim spStringsStrings As Collection          ' 区切った後の文字列(戻り値)中身はString()
    Dim oldCol As Long                          ' 1つ前の区切り文字の位置
    Dim oldRow As Long                          ' 1つ前の区切り文字の行
    Dim row As Long
    Dim col As Long

    ' 初期化
    quotationMarksCount = 0
    Set spStringsStrings1 = New Collection
    oldCol = 0
    oldRow = 1

    For row = 1 To lineStrings.Count
        lineStr = lineStrings.Item(row)
        lineStrLen = Len(lineStr)

        If quotationMarksCount Mod 2 = 0 Then
            oldCol = 0
            oldRow = row
            Set spStrings1 = New Collection
            Call spStringsStrings1.Add(spStrings1)
        End If

        For col = 1 To lineStrLen
            tmpStr = Mid$(lineStr, col, 1)
            If tmpStr = quotationMarks Then
                quotationMarksCount = quotationMarksCount + 1
            ElseIf tmpStr = delimiter Then
                If quotationMarksCount Mod 2 = 0 Then
                    Call spStrings1.Add(midString(lineStrings, oldRow, oldCol + 1, row, col - 1))
                    oldCol = col
                    oldRow = row
                End If
            End If

            If col = lineStrLen Then
                If quotationMarksCount Mod 2 = 0 Or row = lineStrings.Count Then
                    ' ※最後の行については"で終わっていなくても強制的に終了させる
                    Call spStrings1.Add(midString(lineStrings, oldRow, oldCol + 1, row, col))
                End If
            End If
        Next
    Next

    ' 最初と最後の引用符を取り除き、
    ' 引用符が連続している箇所を1つにする
    Set spStringsStrings2 = New Collection

    For row = 1 To spStringsStrings1.Count
        Set spStrings1 = spStringsStrings1.Item(row)
        Set spStrings2 = New Collection
        Call spStringsStrings2.Add(spStrings2)

        For col = 1 To spStrings1.Count
            tmpStr = spStrings1.Item(col)
            If Left$(tmpStr, 1) = quotationMarks And _
                Right$(tmpStr, 1) = quotationMarks Then

                tmpStr = Mid$(tmpStr, 2, Len(tmpStr) - 2)
                Call spStrings2.Add(Replace$(tmpStr, quotationMarks & quotationMarks, quotationMarks))
            Else
                Call spStrings2.Add(tmpStr)
            End If
        Next
    Next

    ' Collectionの中身をString()として返す
    Set spStringsStrings = New Collection

    For row = 1 To spStringsStrings2.Count
        Set spStrings2 = spStringsStrings2.Item(row)

        Call spStringsStrings.Add(strCollectionToStringArray(spStrings2))
    Next

    Set Split_dblQuote = spStringsStrings

End Function

''
' 複数行のデータから、文字列を切り出します
'
' @param lineStrings 1行データの配列(Collectionの中はString)
' @param startRow 開始位置(行番号)
' @param startCol 開始位置(文字数の位置)
' @param endRow 終了位置(行番号)
' @param endCol 終了位置(文字数の位置)
' @return 切り出した文字列。開始位置(行番号)と終了位置(行番号)が
'         異なる場合は1つ以上の改行(vbNewLine)が入ります。

Private Function midString(ByVal lineStrings As Collection, _
    ByVal startRow As Long, ByVal startCol As Long, _
    ByVal endRow As Long, ByVal endCol As Long) As String

    Dim lineStr As String
    Dim row As Long

    If startRow = endRow Then
        lineStr = lineStrings.Item(startRow)
        midString = Mid$(lineStr, startCol, endCol - startCol + 1)
        Exit Function
    End If

    midString = Mid$(lineStrings.Item(startRow), startCol)
    For row = startRow + 1 To endRow - 1
        midString = midString & vbNewLine & lineStrings.Item(row)
    Next

    midString = midString & vbNewLine & Left$(lineStrings.Item(endRow), endCol)

End Function

''
' Collectionの2次元配列(Collectionの中身がCollectionでその中がString)を
' Collectionの1次元配列とString()(Collectionの中身がStringの配列)に変換します。
'
' @param strCollection Collectionの2次元配列
' @return 変換後の配列。Collection内のStringの配列の要素の開始番号は0からです。
'         strCollectionの中で空行(strCollection.Item(i).Count = 0の場合)の
'         Stringの配列の要素の終了番号は-1(この場合でも開始番号は0)となります。

Private Function strCollectionToStringArray(ByVal strCollection As Collection) As Variant

    Dim strings() As String
    Dim i As Long

    If strCollection.Count = 0 Then
        strCollectionToStringArray = Split("", ",")
        Exit Function
    End If

    ReDim strings(0 To strCollection.Count - 1)
    For i = 1 To strCollection.Count
        strings(i - 1) = strCollection.Item(i)
    Next

    strCollectionToStringArray = strings

End Function

(3)エクセルを使って読み込む
 方法が2つあります。
 1つめは[データ]メニューの[外部データの取り込み]-[テキストファイルのインポート](もしくは[データの取り込み...])から行う方法。
 2つめは[ファイル]メニューの[開く]から行う方法です。

 1つめの方法はデータに改行がない場合に使用できます。
 2つ目の方法はデータに改行があっても使用できますが、既に開かれているファイルに書き込むことは出来ません(新しくWorkbookオブジェクトが出来る)。

 どちらの方法もそうですが、直接エクセルのシートに書き込まれるので、読んだときに何らかの処理を行いたい場合にはものすごく不便です。
 えっ、サンプルソースですか?
 記録機能を使って自分で作成するようお願いします・・・(あまりソースだらけにしたくないので・・・。すみません。)。

このページのトップへ

4.データが\でエスケープされる場合

 以下の2つを説明します。
 (1)VBAを使って読む(データに改行がない場合)
 (2)VBAを使って読む(データに改行がある場合)

(1)VBAを使って読む(データに改行がない場合)
 考え方としてはカンマがあったときに、その前の文字がエスケープ文字かどうかを判断するだけです。
 以下、サンプルソースです(注意:サンプルなのでエラー処理などはやっていません。またセル操作での高速化も行っていません)。

サンプルソース
Module4(標準モジュール)
''
' CSVファイル読込みサンプル
'
' このサンプルはデータ内にカンマ、エスケープ文字があっても処理できるようにしてあります。
' ※データ内の改行は正しく認識されません
'
' @author 宮崎 崇 http://darumaexcel.uijin.com/index.html
' @date 2011年1月9日

Option Explicit

''
' CSVファイルを読み込み、アクティブシートに出力します
'
' @param filePath CSVファイルのパス
'

Public Sub readCSV_escapeWithoutCRLF(ByVal filePath As String)

    Dim outputSheet As Worksheet    ' 出力先シート
    Dim outputRow As Long           ' 出力先の行番号
    Dim outputCol As Long           ' 出力先の列番号
    Dim fileNo As Integer           ' ファイル番号
    Dim tmpString As String         ' ファイルから読み込んだ文字列の一時格納場所
#If VBA6 Then
    Dim spString() As String        ' カンマで区切った文字列
#Else
    Dim spString As Variant
#End If

    ' 出力先初期化
    Set outputSheet = ActiveSheet
    outputRow = 1
    outputCol = 1

    ' ファイルを開く
    fileNo = FreeFile
    Open filePath For Input Access Read As #fileNo

    ' ファイルを読み込み、シートに出力
    Do While Not EOF(fileNo)
        Line Input #fileNo, tmpString
        ' カンマで区切る
        spString = Split_escape(tmpString)
        'シートに出力
        For outputCol = 1 To UBound(spString) + 1
            outputSheet.Cells(outputRow, outputCol).Value = spString(outputCol - 1)
        Next

        outputRow = outputRow + 1
    Loop

    ' ファイルを閉じる
    Close #fileNo

End Sub

''
' 文字をカンマで区切ります。
'
' 結果は文字列の一次元配列です。
' 配列の最初の要素番号(LBound)は0です。
' 引数lineStringが空文字の場合はUBoundが-1(LBoundは0)の配列を返します。
'
' 例:
' <pre>
' aaa,bb\,b,c\\cc
'  ↓
' aaa
' bb,b
' c\cc
' </pre>
'
' @param lineString 1行のデータ
' @return 区切った後の文字列(戻り値は定義上はVariant型ですが、実際にはString()を返します)
'

Private Function Split_escape(ByVal lineString As String) As Variant

    Const delimiter As String = ","         ' 区切り文字
    Const escapeMarks As String = "\"       ' エスケープ文字
    Dim strLen As Long                      ' 引数の文字列長
    Dim spStrings1 As Collection            ' 区切った直後の文字列
    Dim spStrings2 As Collection            ' 区切った後、引用符を修正したもの
    Dim col As Long                         ' 文字の位置
    Dim oldCol As Long                      ' 1つ前の区切り文字の位置
    Dim tmpString As String                 ' 一時領域
    Dim strings() As String                 ' 戻り値
    Dim i As Long                           ' ループカウンタ

    ' 初期化
    strLen = Len(lineString)
    Set spStrings1 = New Collection
    Set spStrings2 = New Collection
    oldCol = 0

    ' 空文字の場合
    If strLen = 0 Then
        Split_escape = Split("", delimiter)
        Exit Function
    End If

    col = InStr(1, lineString, delimiter)
    Do While col <> 0
        '一つ前の文字がエスケープ文字ではない場合のみ切り出す
        If Mid$(lineString, col - 1, 1) <> escapeMarks Then
            Call spStrings1.Add(Mid$(lineString, oldCol + 1, col - oldCol - 1))
            oldCol = col
        End If

        col = InStr(col + 1, lineString, delimiter)
    Loop
    Call spStrings1.Add(Mid$(lineString, oldCol + 1, strLen - oldCol))

    ' エスケープを戻す
    For i = 1 To spStrings1.Count
        tmpString = spStrings1.Item(i)

        tmpString = Replace$(tmpString, escapeMarks & delimiter, delimiter)
        tmpString = Replace$(tmpString, escapeMarks & escapeMarks, escapeMarks)

        Call spStrings2.Add(tmpString)

    Next

    ' 一次元配列にして返す
    ReDim strings(0 To spStrings2.Count - 1)
    For i = 1 To spStrings2.Count
        strings(i - 1) = spStrings2.Item(i)
    Next

    Split_escape = strings

End Function

(2)VBAを使って読む(データに改行がある場合)
 考え方はデータに改行が無い場合とほとんど同じですが、やや面倒です。
 以下、サンプルソースです(注意:サンプルなのでエラー処理などはやっていません。またセル操作での高速化も行っていません)。

サンプルソース
Module5(標準モジュール)
''
' CSVファイル読込みサンプル
'
' このサンプルはデータ内にカンマ、エスケープ文字、改行があっても処理できるようにしてあります。
' ※ソースの見易さはデータ内に改行無い場合とした方が上ですし、処理速度、メモリ使用量も
' データ内に改行無い場合とした方が少なくて済みます。
'
' @author 宮崎 崇 http://darumaexcel.uijin.com/index.html
' @date 2011年1月9日

Option Explicit

''
' CSVファイルを読み込み、アクティブシートに出力します
'
' @param filePath CSVファイルのパス
'

Public Sub readCSV_escapeWithCRLF(ByVal filePath As String)

    Dim outputSheet As Worksheet ' 出力先シート
    Dim outputRow As Long ' 出力先の行番号
    Dim outputCol As Long ' 出力先の列番号
    Dim fileNo As Integer ' ファイル番号
    Dim tmpString As String ' ファイルから読み込んだ文字列の一時格納場所
    Dim lineStrings As Collection ' 1行データの配列
    Dim spStringString As Collection ' カンマで区切った後の文字列
#If VBA6 Then
    Dim spString() As String
#Else
    Dim spString As Variant
#End If

    ' 出力先初期化
    Set outputSheet = ActiveSheet

    ' 一行データの配列初期化
    Set lineStrings = New Collection

    ' ファイルを開く
    fileNo = FreeFile
    Open filePath For Input Access Read As #fileNo

    ' ファイルを読み込み
    Do While Not EOF(fileNo)
        Line Input #fileNo, tmpString
        Call lineStrings.Add(tmpString)
    Loop
    ' ファイルを閉じる
    Close #fileNo

    ' カンマで区切る
    Set spStringString = Split_withEscape(lineStrings)

    ' シートに出力する
    For outputRow = 1 To spStringString.Count
        spString = spStringString.Item(outputRow)
        For outputCol = 1 To UBound(spString) + 1
            outputSheet.Cells(outputRow, outputCol).Value = spString(outputCol - 1)
        Next
    Next

End Sub

''
' 1行データの配列をカンマで区切ります。
'
' @param lineStrings 1行データの配列(Collectionの中はString)
' @param カンマで区切った後の文字列の配列。Collection内のStringの配列の要素の開始番号は0からです。
'        strCollectionの中で空行(strCollection.Item(i).Count = 0の場合)の
'        Stringの配列の要素の終了番号は-1(この場合でも開始番号は0)となります。

Private Function Split_withEscape(ByVal lineStrings As Collection) As Collection

    Const delimiter As String = "," ' 区切り文字
    Const escapeMarks As String = "\" ' エスケープ文字
    Const newLineMarks As String = vbNewLine ' 改行コード
    Dim spStringsStrings1 As Collection ' 区切った後文字列。中身はCollection
    Dim spStrings1 As Collection '

    Dim spStringsStrings2 As Collection ' 区切った後文字列。中身はCollection
    Dim spStrings2 As Collection '

    Dim lineStr As String ' 1行データ
    Dim lineStrLen As Long ' 1行データの文字列長

    Dim tmpStr As String

    Dim spStringsStrings As Collection ' 区切った後の文字列(戻り値)中身はString()
    Dim oldCol As Long ' 1つ前の区切り文字の位置
    Dim oldRow As Long ' 1つ前の区切り文字の行
    Dim row As Long
    Dim col As Long

    ' 初期化
    Set spStringsStrings1 = New Collection
    oldCol = 0
    oldRow = 1

    For row = 1 To lineStrings.Count
        lineStr = lineStrings.Item(row)
        lineStrLen = Len(lineStr)

        If row = 1 Then
            oldCol = 0
            oldRow = row
            Set spStrings1 = New Collection
            Call spStringsStrings1.Add(spStrings1)
        Else
            If Right$(lineStrings.Item(row - 1), 1) <> escapeMarks Then
                oldCol = 0
                oldRow = row
                Set spStrings1 = New Collection
                Call spStringsStrings1.Add(spStrings1)
            End If
        End If

        col = InStr(1, lineStr, delimiter)
        Do While col <> 0
            '一つ前の文字がエスケープ文字ではない場合のみ切り出す
            If Mid$(lineStr, col - 1, 1) <> escapeMarks Then
                Call spStrings1.Add(midString(lineStrings, oldRow, oldCol + 1, row, col - 1))
                oldCol = col
                oldRow = row
            End If

            col = InStr(col + 1, lineStr, delimiter)
        Loop

        If Right$(lineStr, 1) <> escapeMarks Then
            Call spStrings1.Add(midString(lineStrings, oldRow, oldCol + 1, row, lineStrLen))
            oldCol = col
            oldRow = row
        End If
    Next

    ' エスケープを戻す
    Set spStringsStrings2 = New Collection

    For row = 1 To spStringsStrings1.Count
        Set spStrings1 = spStringsStrings1.Item(row)
        Set spStrings2 = New Collection
        Call spStringsStrings2.Add(spStrings2)

        For col = 1 To spStrings1.Count
            tmpStr = spStrings1.Item(col)

            tmpStr = Replace$(tmpStr, escapeMarks & delimiter, delimiter)
            tmpStr = Replace$(tmpStr, escapeMarks & newLineMarks, newLineMarks)
            tmpStr = Replace$(tmpStr, escapeMarks & escapeMarks, escapeMarks)

            Call spStrings2.Add(tmpStr)

        Next
    Next

    ' Collectionの中身をString()として返す
    Set spStringsStrings = New Collection

    For row = 1 To spStringsStrings2.Count
        Set spStrings2 = spStringsStrings2.Item(row)

        Call spStringsStrings.Add(strCollectionToStringArray(spStrings2))
    Next

    Set Split_withEscape = spStringsStrings

End Function

''
' 複数行のデータから、文字列を切り出します
'
' @param lineStrings 1行データの配列(Collectionの中はString)
' @param startRow 開始位置(行番号)
' @param startCol 開始位置(文字数の位置)
' @param endRow 終了位置(行番号)
' @param endCol 終了位置(文字数の位置)
' @return 切り出した文字列。開始位置(行番号)と終了位置(行番号)が
'         異なる場合は1つ以上の改行(vbNewLine)が入ります。

Private Function midString(ByVal lineStrings As Collection, _
    ByVal startRow As Long, ByVal startCol As Long, _
    ByVal endRow As Long, ByVal endCol As Long) As String

    Dim lineStr As String
    Dim row As Long

    If startRow = endRow Then
        lineStr = lineStrings.Item(startRow)
        midString = Mid$(lineStr, startCol, endCol - startCol + 1)
        Exit Function
    End If

    midString = Mid$(lineStrings.Item(startRow), startCol)
    For row = startRow + 1 To endRow - 1
        midString = midString & vbNewLine & lineStrings.Item(row)
    Next

    midString = midString & vbNewLine & Left$(lineStrings.Item(endRow), endCol)

End Function

''
' Collectionの2次元配列(Collectionの中身がCollectionでその中がString)を
' Collectionの1次元配列とString()(Collectionの中身がStringの配列)に変換します。
'
' @param strCollection Collectionの2次元配列
' @return 変換後の配列。Collection内のStringの配列の要素の開始番号は0からです。
'         strCollectionの中で空行(strCollection.Item(i).Count = 0の場合)の
'         Stringの配列の要素の終了番号は-1(この場合でも開始番号は0)となります。

Private Function strCollectionToStringArray(ByVal strCollection As Collection) As Variant

    Dim strings() As String
    Dim i As Long

    If strCollection.Count = 0 Then
        strCollectionToStringArray = Split("", ",")
        Exit Function
    End If

    ReDim strings(0 To strCollection.Count - 1)
    For i = 1 To strCollection.Count
        strings(i - 1) = strCollection.Item(i)
    Next

    strCollectionToStringArray = strings

End Function

このページのトップへ

5.サンプルのダウンロードと実行

 readCSV.zip(33kB)

実行(試す)場合は、イミディエイトウィンドウから以下のようにして実行してください。

Call Module1.readCSV_simple(ThisWorkbook.Path & Application.PathSeparator & "testCSVSimple.txt")

このページのトップへ

6.参考資料など

 このページを作成するときに参考にしたページなどです。

リンク先の名称
リンク先の説明
リンクした日
MySQL :: MySQL 4.1 リファレンスマニュアル :: 6.4.8 LOAD DATA INFILE 構文
MySQLのデータ読み込み構文のマニュアル
2011/1/10
Comma-Separated Values - Wikipedia
CSVについての説明です
2011/1/10
Prev Up Next  Top
このページのトップへ

このページの利用によって発生した、いかなる損害について、このホームページの作成者は責任を負いません。
このページの間違いや嘘を見つけた方、このページに書いて欲しい情報がある方はメールをお願いします。

Microsoft 、Windows 、Visual Basic および Excel は米国Microsoft Corporationの米国およびその他の国における登録商標または商標です。
ここではExcel® をエクセル、Visual Basic® for Applications をVBAと表記する場合があります。
Mac 、Mac OS 、Mac OS X は米国Apple Computer,Inc.の登録商標または商標です。
その他、社名および商品名、システム名称などは、一般に各社の商標または登録商標です。

このホームページの作成者はこれらの会社とはいっさい関係がありません。