はじめに
ADODB.StreamオブジェクトのSaveToFileメソッドでUTF-8形式のファイルを作成した場合、ファイルの先頭にBOM(Byte Order Mark)が付加されてしまいます。
この記事では、BOMを付加せずに、UTF-8のファイルを作成する方法を紹介します。
2009.12.13追記
一時ファイルを作成しなくても、BOMなしのUTF-8ファイルを出力する方法が見つかりました。
BOMなしのUTF-8ファイルを作成するには、まずBOM付きの一時ファイルを作成します。そして、一時ファイルをバイナリモードで読み込み、BOMの部分(先頭3バイト)を読み飛ばした4バイト目からバイナリモードで目的のファイルに書き込むという手法を用います。
一応説明しておくと、一度Streamに対してUTF-8でデータを書き込みます。その後で、バイナリとして最初の3バイト(BOM分)を読み飛ばしてバイナリを読み出します。読み出したバイナリを新しいStreamにバイナリとして食わせてファイルに保存しました。
JavaScriptでファイルの書き込みをUTF-8で行う(htaまたは、wsh用) - jiroの日記
上記のサイトは、JavaScriptで書かれていますが、僕はVBScriptでリライトしてみました。
BOMなしのUTF-8でファイルに書き込む
Sub saveFile(filename, text) On Error Resume Next ' ADODB.Streamのモード Dim adTypeBinary : adTypeBinary = 1 Dim adTypeText : adTypeText = 2 Dim adSaveCreateOverWrite : adSaveCreateOverWrite = 2 ' ADODB.Streamを作成 Dim pre : Set pre = CreateObject("ADODB.Stream") ' 最初はテキストモードでUTF-8で書き込む pre.Type = adTypeText pre.Charset = "UTF-8" pre.Open() pre.WriteText(text) ' バイナリモードにするためにPositionを一度0に戻す ' Readするためにはバイナリタイプでないといけない pre.Position = 0 pre.Type = adTypeBinary ' Positionを3にしてから読み込むことで最初の3バイトをスキップする ' つまりBOMをスキップします pre.Position = 3 Dim bin : bin = pre.Read() pre.Close() ' 読み込んだバイナリデータをバイナリデータとしてファイルに出力する ' ここは一般的な書き方なので説明を省略 Dim stm : Set stm = CreateObject("ADODB.Stream") stm.Type = adTypeBinary stm.Open() stm.Write(bin) stm.SaveToFile filename, adSaveCreateOverWrite ' force overwrite stm.Close() End Sub
一時ファイルを用いてBOMなしUTF-8ファイルを作成する方法
「UTF-8」を指定した場合、BOM付きのUTF-8で保存されてしまう。
BOMなしUTF-8は指定できないため、BOMなしにするにはBOMを除去する処理が必要になる。
具体的には以下のような処理を行う。
1. 一時ファイルに「UTF-8(BOM付き)」で出力する
2. 一時ファイルを4バイト目から読み込む(BOMが先頭3バイトのため)
3. 読み込んだ4バイト目以降を、出力ファイルに書き出す
4. 一時ファイルを削除する
忘れな〜い録 UTF-8(BOMなし)でファイルを保存する。(VBA)
上記のサイトは、VBAで書かれていますが、僕はVBScriptで書いてみました。
UTF-8ファイルの書き込み用クラス
Class UTF8FileWriter Private name, errNo, errDesc ' コンストラクタ Private Sub Class_Initialize name = "UTF8FileWriter" End Sub ' UTF-8で保存する(BOMあり) Public Sub WriteAll(text, fileName) On Error Resume Next Dim medthodName : medthodName = name & "." & "WriteAll" With CreateObject("ADODB.Stream") .Type = 2 .Charset = "UTF-8" .Open .writeText text .SaveToFile fileName, 2 .Close End With If Err.Number <> 0 Then errNo = Err.Number errDesc = Err.Description On Error GoTo 0 Call Err.Raise(errNo, medthodName, errDesc) End If End Sub ' UTF-8で保存する(BOMなし) Public Sub WriteAllWithoutBOM(text, fileName) On Error Resume Next Dim medthodName : medthodName = name & "." & "WriteAllWithoutBOM" Dim tmpFile : tmpFile = fileName & ".tmp" ' 一時ファイルにUTF-8形式で書き込む Call WriteAll(text, tmpFile) If Err.Number <> 0 Then errNo = Err.Number errDesc = Err.Description On Error GoTo 0 Call Err.Raise(errNo, medthodName, errDesc) End If With CreateObject("ADODB.Stream") .Type = 1 .Open .LoadFromFile(tmpFile) ' 一時ファイルをバイナリで読み取る .Position = 3 ' BOMの3バイトを読み飛ばす ' 4バイト目から目的のファイルにバイナリで書き込む Dim ws : Set ws = CreateObject("ADODB.Stream") ws.Type = 1 ws.Open ws.Write(.Read(-1)) ws.SaveToFile fileName, 2 ws.Close .Close End With If Err.Number <> 0 Then errNo = Err.Number errDesc = Err.Description On Error GoTo 0 Call Err.Raise(errNo, medthodName, errDesc) End If ' 一時ファイルの削除 Call CreateObject("Scripting.FileSystemObject").DeleteFile(tmpFile) If Err.Number <> 0 Then errNo = Err.Number errDesc = Err.Description On Error GoTo 0 Call Err.Raise(errNo, medthodName, errDesc) End If End Sub End Class
呼び出し方
Dim fw : Set fw = New UTF8FileWriter Call fw.WriteAllWithoutBOM("ほげ","out.txt")