BOMなしUTF-8ファイルの読み書きをするUTF8FileSystemクラス

エラーが起きたときにテキストファイルにログを書いたり、JVDataレコードのテストデータをテキストファイルに書くなど、テキストファイルの操作が必要な場面があるので、専用のVBAのクラスモジュールを作りました。

文字コードはUTF-8で、BOM[1]なし、改行コードLfのテキストファイルを読み書きするようにしました。Linuxでよく使われるテキストです。Windowsのメモ帳では正しく開けません。私はAtom、Visual Studio Code、Emeditorを使っています。

クラスモジュールの名前はUTF8FileSystemとしました。コードは次のとおりです。

' 2017-05-05 (c) Kosuke Maeda
' Class Name:  UTF8FileSystem
' Property:    FilePath
' Method:      ReadText, WriteText, Remove
' Description: BOMなしUTF8ファイルのテキスト読み込みと書き込みを行うクラス

Option Explicit

' プライベート変数定義
Private m_FilePath    As String
Private m_ADODBStream As ADODB.Stream
Private m_FileSystem  As Scripting.FileSystemObject


Private Sub Class_Initialize()

'  ' ライブラリの参照設定をしていない場合のオブジェクト宣言と生成
'  Dim ADODBStream As Object
'  Dim FileSystem  As Object
'  Set ADODBStream = CreateObject("ADODB.Stream")
'  Set FileSystem = CreateObject("Scripting.FileSystemObject")
  
  Set m_FileSystem = New Scripting.FileSystemObject
  Set m_ADODBStream = New ADODB.Stream

  m_ADODBStream.Charset = "UTF-8"  ' UTF-8(新規作成保存・追加保存どちらも先頭にBOMコードが付加される)
  m_ADODBStream.LineSeparator = 10 ' 10: LF
  m_ADODBStream.Mode = 3           ' 3: 読み取り/書き込みモード
  m_ADODBStream.Type = 2           ' 2: テキストデータ

End Sub

Private Sub Class_Terminate()

  Set m_ADODBStream = Nothing
  Set m_FileSystem = Nothing

End Sub


' ファイルパス
Public Property Get FilePath() As String
  
  FilePath = m_FilePath

End Property

Public Property Let FilePath(ByRef Value As String)
  
  m_FilePath = Value

End Property


' ファイルm_FilePathにpvTextを書き込む(文字エンコードBOMなしUTF-8, 改行コードLF)
' ファイルが既存の場合は追記する
Public Sub WriteText(ByRef Text As String)
  
  Dim Buffer As Variant

  If m_FilePath = "" Or Text = "" Then
    Exit Sub
  End If

  m_ADODBStream.Open

  ' ファイルが存在する場合はテキストをロードする
  ' ファイルがUTF-8以外の文字エンコードだと文字化けする
  If m_FileSystem.FileExists(m_FilePath) Then
    m_ADODBStream.LoadFromFile m_FilePath
    m_ADODBStream.Position = m_ADODBStream.Size ' ポインタを終端へ移動する
  End If

  m_ADODBStream.WriteText Text, 0  ' 0: 最後に改行コードを書き込まない 1: 第1引数の文字列と改行コードを書き込む

  ' BOM(先頭3Bytes)をスキップして出力したファイルを読み込む
  m_ADODBStream.Position = 0   ' Typeの変更にはPosition=0とする必要がある
  m_ADODBStream.Type = 1       ' 1: バイナリデータ
  m_ADODBStream.Position = 3
  Buffer = m_ADODBStream.Read
         
  ' ストリームの先頭に戻って内容を再度書き出す
  m_ADODBStream.Position = 0
  m_ADODBStream.Write Buffer
  m_ADODBStream.SetEOS         ' Streamオブジェクトの余分なバイトを削除する
  m_ADODBStream.SaveToFile m_FilePath, 2

  m_ADODBStream.Type = 2  ' 2: テキストデータに戻す
  m_ADODBStream.Close

End Sub


' ファイルm_FilePathのテキストを返す(文字エンコードUTF-8)
Public Function ReadText() As String

  If m_FileSystem.FileExists(m_FilePath) Then
    m_ADODBStream.Open
    m_ADODBStream.LoadFromFile m_FilePath
    ReadText = m_ADODBStream.ReadText
    m_ADODBStream.Close
  End If

End Function


' ファイルを削除する
Public Sub Remove()

  If m_FileSystem.FileExists(m_FilePath) Then
    m_FileSystem.DeleteFile m_FilePath, True
  End If

End Sub

FilePathプロパティにテキストファイルのパスを設定します。親フォルダを新規作成する機能は作ってないので、親フォルダが既存のパスを設定します。

String型変数の引数をテキストファイルに書き込むWriteTextメソッド、テキストファイルを読み込みString型変数を返すReadTextメソッド、ファイルを削除するRemoveメソッドがあります。

テキストファイルの読み書きはADODB.Streamオブジェクトを使っています[2]

このUTF8FileSystemクラスは次のように使います。

Sub WriteReadText()

  Dim UTF8FileSystem As UTF8FileSystem
  Dim Text as String

  ' オブジェクトを生成する  
  Set UTF8FileSystem = New UTF8FileSystem

  ' ファイルのパスを設定する
  UTF8FileSystem.FilePath = ThisWorkbook.path & "\test-utf8filesystem.txt"

  ' ファイルに文字列を書き込む  
  UTF8FileSystem.WriteText Now & vbLf
  
  ' 書き込んだ文字列を読み込む
  Text = UTF8FileSystem.ReadText
  Debug.Print Text

  ' オブジェクトを消去する(メモリリリースする)
  Set UTF8FileSystem = Nothing

End Sub
2017/08/08 09:37:12
(改行)

Excelワークブックがあるフォルダにテキストファイルを作成し、文字列、実行したときの時刻を書き込んで、読み込んだテキストをイミディエイトウィンドウに表示しています。

また、WriteTextメソッドは、ファイルがすでに存在する場合は、文字列を文末に追加する仕様です。ファイルを新規作成して、テキストを書き込みたいときは、はじめにRemoveメソッドでファイルを削除します。

Private Sub RemoveFile()

  Dim UTF8FileSystem As UTF8FileSystem
  
  Set UTF8FileSystem = New UTF8FileSystem
  
  UTF8FileSystem.FilePath = ThisWorkbook.path & "\test-utf8filesystem.txt"

  ' ファイルを新規作成して書き込む
  UTF8FileSystem.Remove
  UTF8FileSystem.WriteText "Something"

  Set UTF8FileSystem = Nothing

End Sub

また、UTF-8テキストファイルをBOMなしにするために、一度BOM付きのテキストファイルを作成して、バイナリモードで先頭3 Bytesを除いてファイルを読み込み、再度書き込みしています。

ほぼ同じデータを2回テキストファイルに出力しているので手間なのですが、あえてBOMなしにしているのは、ADOがBOMありだと文字化けするからです。例えば、ADODB.ConnectionとADODB.RecordSetでUTF-8のCSVファイルをデータベースとみなして操作する場合、BOM付きのSchema.iniファイル[3]だと1列目の列名にBOMコードが含まれて文字化けしてしまいます。


  1. バイトオーダーマーク (byte order mark)。ファイルの先頭にある3 Bytesの情報で、そのテキストファイルの文字コードがUnicodeであることを表します。UTF-8はUnicodeの1つ。 ↩︎

  2. ADOのメソッド、プロパティなどはADO API リファレンス - Microsoft MSDNに詳しい説明があります。 ↩︎

  3. CSVファイルをデータベース操作するときに便利なスキーマ定義ファイルについてSchema.ini File (Text File Driver) - Microsoft Docsに説明があります。 ↩︎

Kosuke Maeda / まえだこうすけ

「機械学習で競馬予想して勝てるのか?」をテーマに活動中! QiitaにはR、VBAなどのTipsを投稿しています。