ファイル操作

【VBA】UTF-8でテキストファイルを読み書きする方法

更新日:2023/12/20

UTF-8で書き込まれたテキストファイルを、VBAのOpenメソッドで操作すると文字化けします。
外部から持ち込まれたファイルはUTF-8のケースが多いので困るケースがあります。
そこで今回は、UTF-8ファイルを読み書きする方法をお伝えします

 

UTF-8は文字化けする

VBAは内部的にUTF-16(Unicode)が使用されています。
一方、VBエディタはシフトJISで内容が記憶されます。
整合性を取るために、実行時にコードがUTF-16に変換されます。

同様にOpenステートメントで開かれたテキストファイルは、読み込み時にシフトJISからUTF-16に変換され、書き込み時はUTF-16からシフトJISに変換されます。
このようにOpenステートメントは文字コードがシフトJISという前提で動作しています。
そのため、他の文字コード、例えばUTF-8を読み込むと変換に失敗して文字化けします。

そこで読み書きの際に、UTF-8からUTF-16への変換、またはその逆変換を行ってくれるライブラリを使用することで、UTF-8でテキストファイルを読み書きすることが可能となります。

その手段として今回は、Microsoft ActiveX Data Objects Library(ADODB)のStreamオブジェクトを使用します。

 

操作イメージ

Streamオブジェクトは、内容を記憶しておくバッファを持っています。
Streamオブジェクトのファイル読み込みメソッドを実行すると、このバッファにファイルの内容が保存されます。
このときファイルの文字形式がUTF-8指定されていると、UTF-8からUTF-16へ変換されます。

その後、Streamオブジェクトの読み込みメソッドを実行すると、バッファからデータを取り出します。
取り出しは一度にまとめてでも、数回に分けてでも、どちらでも可能です。

Streamオブジェクトからの読み込み

ファイルへの書き込みは、書き込みメソッドでバッファに書き込みます。
書き込みは数回に分けることも可能です。

その後、ファイル書き込みメソッドを実行すると、バッファの内容がファイルに書き込まれます。
このときファイルの文字形式がUTF-8指定されていると、UTF-16からUTF-8へ変換されます。

 

ADODB.Streamオブジェクトの取得

UTF-8ファイルを読み書きするには、まずはADODB.Streamオブジェクトを取得する必要があります。

ADODB.Streamオブジェクトの取得方法は二つあります。
一つは、文字列"ADODB.Stream"をCreateObject関数に渡す方法です。

Dim fso As Object
Set fso = CreateObject("ADODB.Stream")

もう一つはVBエディタのメニュー:ツールから参照設定画面を開いて『Microsoft ActiveX Data Objects XX.X Library』を参照設定してから次のコードを実行する方法です。
※XX.Xは数値です。複数あるので一番値が大きいものを選択します。

Dim adoStream As New ADODB.Stream

参照設定をおこなうと、コード入力時にメソッド名候補が表示されます。
入力ミスを防げるので便利です。

 

UTF-8ファイルの読み込み

ADODB.StreamオブジェクトでのUTF-8ファイル読み込は、一度に全ての内容を処理する方法と、一行ずつ処理する方法の二つあります。
その二つの方法について解説します。

全て読み込み

UTF-8ファイルの全ての内容を変数に読み込むときは、次のような流れでコードを記述します。

With ADODB.Streamオブジェクト .Open .Type = adTypeText .Charset = "UTF-8" .LoadFromFile ファイルのパス 変数 = .ReadText .Close End With

まずは、Openメソッドでストリームを開きます。
次に、テキストかバイナリかをTypeプロパティで指定します。
設定できる値はStreamTypeEnum型です。

Charsetプロパティは、テキストファイルの文字セットを文字列で指定します。
このプロパティは、TypeプロパティがadTypeTextの時のみ有効です。

LoadFromFileメソッドは、既存ファイルの内容を読み込みます。
ファイルが存在しない場合はエラーです。

ReadTextメソッドは引数として取り出すバイト数を受け取り、そのバイト数だけ読み込んだデータを返します。
またはStreamReadEnum型を受け取り、全てのデータまたは行単位でデータを返します。
引数が省略されると、全てのデータを返します。

最後にCloseメソッドで、ストリームを閉じます。

Sub readTextTest1()
    Const fileName As String = "C:\Users\xxx\Documents\text.txt"
    Dim adoStream As New ADODB.Stream
    
    Dim textData As String
    
    With adoStream
        .Open
        .Type = adTypeText
        .Charset = "UTF-8"
        .LoadFromFile fileName
        textData = .ReadText
        .Close
    End With

    Debug.Print textData
End Sub

1行ずつ読み込み

UTF-8ファイルの内容を1行ずつ変数に読み込むときは、次のような流れでコードを記述します。

With ADODB.Streamオブジェクト .Open .Type = adTypeText .Charset = "UTF-8" .LineSeparator = 改行種類 .LoadFromFile ファイルのパス Do Until .EOS 変数 = .ReadText(adReadLine) Loop .Close End With

全て読み込みのケースとほぼ同じです。

異なるのはLineSeparatorプロパティで改行の種類を指定している点です。
LineSeparatorプロパティは、LineSeparatorsEnum型の値を指定します。
実際のファイルの改行コードと一致しない場合、全ての行を一度に読み取ったり、CRまたはLFが読み取ったデータに含まれる等の不具合が生じます。
改行コードが不明なときは、全て読み込み後にReplaceで改行コードを統一してSplitで分割するか、一文字単位で改行を確認しながら読み込むなどの工夫が必要です。

EOSプロパティは、現在位置がストリームの終端まで達するとTrueを返します。
そのため、ReadTextメソッドで1行読み込み、EOSプロパティがTrueを返すまでループします。

Sub readTextTest2()
    Const fileName As String = "C:\Users\xxx\Documents\text.txt"
    Dim adoStream As New ADODB.Stream
    
    With adoStream
        .Open
        .Type = adTypeText
        .Charset = "UTF-8"
        .LineSeparator = adCRLF
        .LoadFromFile fileName

        Do Until .EOS
            Debug.Print .ReadText(adReadLine)
        Loop
        .Close
    End With
End Sub

 

UTF-8ファイルの書き込み

ADODB.StreamオブジェクトでのUTF-8ファイル書き込みは、その都度書き込む方法と、一行ずつ書き込む方法追記で書き込む方法の二つあります。
その二つの方法について解説します。

その都度書き込む

UTF-8ファイルに文字をその都度書き込むときは、次のような流れでコードを記述します。

With ADODB.Streamオブジェクト .Open .Type = adTypeText .Charset = "UTF-8" .WriteText 文字列 ・・・任意の回数書き込み .SaveToFile ファイルのパス, adSaveCreateOverWrite .Close End With

ストリームを開いた後、Typeプロパティでテキストモード(StreamTypeEnum型)に、Charsetプロパティにテキストファイルの文字コードを指定します。

WriteTextメソッドにファイルに書き込む文字列を渡します。
これは、任意の回数に分けて実行できます。

SaveToFileメソッドでファイルに書き込みます。
その際、二つ目の引数で上書きするかどうかを指定できます。
指定する値はSaveOptionsEnum型から一つです。
出力先のファイルは、VBAと関連付けられません。
出力後の他アプリで変更や削除を行えます。

最後にCloseメソッドで、ストリームを閉じます。

Sub writeTextTest1()
    Const fileName As String = "C:\Users\xxx\Documents\text.txt"
    Dim adoStream As New ADODB.Stream
    
    With adoStream
        .Open
        .Type = adTypeText
        .Charset = "UTF-8"

        .WriteText "こんにちは"
        .WriteText "よろしくおねがいします"
        
        .SaveToFile fileName, adSaveCreateNotExist

        .Close
    End With
    Call readTextTest1
    
End Sub

最後のreadTextTest1は、全て読み込みで紹介しているコードです。
上のコードを実行すると、イミディエイトウィンドウに次のような文字列が表示されます。

こんにちはよろしくおねがいします

一行ずつ書き込む

UTF-8ファイルに文字を一行ずつ書き込むときは、次のような流れでコードを記述します。

With ADODB.Streamオブジェクト .Open .Type = adTypeText .Charset = "UTF-8" .LineSeparator = 改行コード .WriteText 文字列 , adWriteLine ・・・任意の回数書き込み .SaveToFile ファイルのパス, adSaveCreateOverWrite .Close End With

その都度書き込むのケースとほぼ同じです。

相違点はLineSeparatorプロパティで改行で使用するコードを指定している点です。
LineSeparatorプロパティは、LineSeparatorsEnum型の値を指定します。

またWriteTextメソッドで書き込むとき、第二引数にadWriteLine(SaveOptionsEnum型)を指定します。
これにより文字列を書き込み後に、LineSeparatorプロパティで指定した改行コードが書き込まれます。

Sub writeTextTest2()
    Const fileName As String = "C:\Users\xxx\Documents\text.txt"
    Dim adoStream As New ADODB.Stream

    With adoStream
        .Open
        .Type = adTypeText
        .Charset = "UTF-8"
        .LineSeparator = adCRLF

        .WriteText "こんにちは", adWriteLine
        .WriteText "よろしくおねがいします", adWriteLine
        
       .SaveToFile fileName, adSaveCreateNotExist
        .Close
    End With
    Call readTextTest1
    
End Sub

最後のreadTextTest1は、全て読み込みで紹介しているコードです。
上のコードを実行すると、イミディエイトウィンドウに次のような文字列が表示されます。

こんにちは
よろしくおねがいします

追記で書き込む方法

UTF-8ファイルに追記書き込みするときは、次のような流れでコードを記述します。

With ADODB.Streamオブジェクト .Open .Type = adTypeText .Charset = "UTF-8" .LoadFromFile ファイルのパス .Position = .size .WriteText 文字列 ・・・任意の回数書き込み .SaveToFile ファイルのパス, adSaveCreateOverWrite .Close End With

全て読み込みで紹介した方法でファイルを読み込んだ後、現在位置をストリームの末尾に移動します。
次に、その都度書き込むで紹介した方法でファイルに書き込みます。
1行単位で書き込むときは、一行ずつ書き込むを参考にしてください。

Streamオブジェクトにはストリームの末尾に移動するSetEOSメソッドがありますが、ここでは上手く動作しません。
そのため、現在位置のストリーム末尾への移動は、Positionプロパティにバッファのサイズをセットしています。

Sub writeAddTextTest1()
    Const fileName As String = "C:\Users\xxx\Documents\text.txt"
    Dim adoStream As New ADODB.Stream
    
    Debug.Print "【追記前】"
    Call readTextTest1

    With adoStream
        .Open
        .Type = adTypeText
        .Charset = "UTF-8"
        .LineSeparator = adCRLF

        .Position = .size
        .WriteText vbCrLf & "よろしくお願いします!"
        
        .SaveToFile fileName, adSaveCreateNotExist

        .Close
    End With
    Debug.Print "【追記後】"
    Call readTextTest1
    
End Sub

最後のreadTextTest1は、全て読み込みで紹介しているコードです。
上のコードを実行すると、イミディエイトウィンドウに次のような文字列が表示されます。

【追記前】
こんにちは!
【追記後】
こんにちは!
よろしくお願いします!

 

補足:関連する型定義

このページで使用されている型の定数と値を紹介します。

StreamTypeEnum型

StreamTypeEnum型は、Typeプロパティで使用される列挙型です。

■StreamTypeEnum型
定数意味
adTypeBinary1バイナリファイル
adTypeText2テキストファイル

StreamReadEnum型

StreamReadEnum型は、ReadTextメソッド等で使用される列挙型です。

■StreamReadEnum型
定数意味
adReadAll-1全て読み込む
adReadLine-21行読み込む

LineSeparatorsEnum 型

LineSeparatorsEnum 型は、LineSeparatorプロパティで使用される列挙型です。

■LineSeparatorsEnum 型
定数意味
adCRLF-1改行コードはCRLF
adLF10改行コードはLF
adCR13改行コードはCR

SaveOptionsEnum型

SaveOptionsEnum型は、SaveToFileメソッドで使用される列挙型です。

■LineSeparatorsEnum 型
定数既存ファイルの状況
存在しない存在する読込専用
adSaveCreateNotExist1作成するエラーエラー
adSaveCreateOverWrite2作成する上書きするエラー

更新日:2023/12/20

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。