UTF-8のCSVファイルを扱う

ファイル

UTF-8のCSVファイルを扱う

 ADODB.Streamオブジェクトの扱い方として、ライブラリの読み込み方、テキストファイルの扱い方を紹介してきました。
 今回は、CSVファイルの扱い方を紹介します。

簡単な方法

 実は以前、「CSVファイルを読み込む」という記事の中で、CSVファイルを開いてExcelファイルにデータを取り込む方法を紹介しました。この方法ですと、Shift-JISのCSVファイルでも、UTF-8のCSVファイルでも字化けせずに読み込んでいます。
 ただ問題は、セルに代入した時の自動変換です。
 具体的には、「1-2」が「1月2日」などと解釈されてしまいます。

 この対策としては、一つひとつの項目に注意しながら取り込んでいくしかありません。
 セルへ代入する際の自動変換については考慮していませんので、自動変換を回避する方法に関しては、こちらの記事を参考にしてみてください。

UTF-8のCSVファイルを読み込む

 UTF-8のCSVファイルを読み込んで、セルに値を代入するプログラムです。
 CSVファイルから一行づつ取り込んで、各行をSprit関数でカンマを区切り文字として配列に切り出しています。

Sub ReadUTF8_CSV()
    Dim filename
    filename = Application.GetOpenFilename(FileFilter:="CSVファイル(*.CSV),*.CSV", _
                                                Title:="CSVファイルの選択")
    If filename = False Then Exit Sub
    
    Dim myADO As Object
    Set myADO = CreateObject("ADODB.Stream")    '①
    
    Dim i As Long, j As Long
    Dim bufLine As String
    Dim myArr
    i = 1
    With myADO            '②
        .Type = 2 'adTypeText
        .Charset = "UTF-8"
        .Open
        .LoadFromFile (filename)
        Do Until .EOS      '③
            bufLine = .ReadText(-2)  'adReadLine   '④
            myArr = Split(bufLine, ",")    '⑤
            j = 0
            For j = 0 To UBound(myArr)    '⑥
                Cells(i, j + 1) = myArr(j)
            Next
            i = i + 1
        Loop
        .Close
    End With
End Sub

 最初に①でADODB.Streamオブジェクトを読み込んでいます。今回はCreateObject()関数で読み込んでいますので受け側の変数はObject型となり、ADODB.Streamオブジェクトの定数は使えませんので数値で指定する必要があります。
 (詳しくは、「ライブラリを読み込む2つの方法」の記事を参照してください。)

 ②で、ADODB.Streamオブジェクトのプロパティメソッドを設定します。
 Typeは、2 ( =adTypeText )でテキストファイルとして読み込む事を指示しています。
 これはデフォルト値なので省略可能です。
 
 Charset で文字セットを “UTF-8” に指定しています。ダブルクォーテーションで囲って文字列として指定します。
 
 Openで ADODB.Stream オブジェクトを開きます。
 LoadFromFile で先程選択したファイルを開きます。
 
 今回は、③のように Do Until .EOS としてファイルの終端まで繰り返すようにしています。
 EOS がファイルの終端になります。
 
 ループの中の④で、一行づつ文字列を取り出しています。
 ReadText(-2) とする事で1行分の文字列を取り出し、次に呼ばれた時は次の行が取り出せます。
 -2 (=adReadLine) は一行づつ取り出すためのオプションです。

 ⑤は、Split関数を使って一行分のテキストを区切り文字のカンマで切り出しています。
 myArrは、切り出した配列が入ります。
 
 ⑥のループで、配列の数だけセルの代入を行います。

 最後は Close でオブジェクトを閉じます。

UTF-8のCSVファイルに書き込む

 セルの値をCSVファイルに書込むには、行ごとに列の値をカンマで区切りながら結合します。
 行末の改行はWriteTextメソッドで追加可能です。

Sub WriteUTF8_CSV()
    Dim filename     '①
    filename = ThisWorkbook.Path & "\UTF8test.csv"
    
    Dim myADO As Object
    Set myADO = CreateObject("ADODB.Stream")
    
    Dim i As Long, j As Long
    Dim myArr
    Dim bufLine As String
    i = 1
    
    With myADO
        .Type = 2 'adTypeText
        .Charset = "UTF-8"
        .Open
        
        For i = 1 To ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row  '②
            bufLine = ""
            For j = 1 To ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column  '③
            
                If j > 1 Then
                    bufLine = bufLine & ","    '④
                End If
                bufLine = bufLine & Cells(i, j)  '⑤
            Next j
            .WriteText bufLine, 1   'adWriteLine(文字列と改行文字を書込む)  '⑥
        Next i
        
        .SaveToFile filename, 2  'adSaveCreateOverWrite   '⑦
        .Close
    End With
End Sub

 ①で書き込むファイル名を設定します。 ThisWorkbook.Path は現在開いているExcelファイルと同じパスが得られます。&(アンパーサント) でファイル名とつなぎますが、ファイル名の前に \ を付けるのを忘れないようにしましょう。

 ②のForループは行のループです。ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row で最終行を取得しています。
 ③のForループは列のループで、今度は、ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column として最終列を取得しています。
 ④で、区切り文字のカンマを追加していますが、先頭にカンマが入らないように、If j > 1 Then としています。
 ⑤でセルの値を文字列として追加しています。もし加工が必要ならこの直前で行うと良いでしょう。
 ⑥は、一行分の書き込みになります。WriteText メソッドは改行文字も追加してくれます。
 全てのループが終了したら⑦の SaveToFile メソッドでファイルを保存します。
 最後は Close でオブジェクトを閉じます。

まとめ

 今回は、UTF-8のCSVファイルの扱いについて見てきました。
 CSVファイルでは、日付のような形式では、自動変換が起こってしまうため対策が必要になってきます。 
 その他にも区切り文字が項目内の文字列として含まれている場合などは誤動作してしまうケースも考えられます。

 123,”Yes,No”,abc

 上記の例では、3項目になるべきですが、2項目目の文字列のカンマを区切り文字と誤認識してしまいます。
 冒頭で紹介した「CSVファイルを読み込む」という記事の方法では、ExcelがCSVファイルを読み込む際に上手くやってくれるのですが、イチからプログラムを組むような場合ではこのようなケースが想定される場合は対策しなければならないでしょう。 
 このようにCVSファイルの読み込みに関しては、単に区切り文字で分ければ良いという事で解決するわけではないのです、複雑さに対応するか否かは、例外に対して複雑になっても対応するか、無視して楽するかのトレードオフになると思います。

タイトルとURLをコピーしました