【実務・中級編】【文字コード問題】FSOのShift_JIS制限とADODB.Streamを組み合わせたUTF-8ファイルの安全な読み書き – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptを掌握する極限の知見:FSOの呪縛を解く。ADODB.Streamで実現する完全無欠のUTF-8入出力

開発現場でいまだに現役として稼働し続けるVBScript。だが、その標準機能であるFileSystemObject(以下、FSO)をそのまま現代のシステム連携に使うことは、地雷原を目隠しで歩くようなものだ。

特に「文字コード」。
FSOの `OpenTextFile` メソッドは、デフォルトではANSI(Shift_JIS等)でファイルを読み書きする。引数に `-1`(Unicode / UTF-16LE)を指定すれば広範な文字を扱えるようになるが、現代のWeb APIやモダンな設定ファイルの主流である UTF-8(特にBOMなし) を扱おうとした瞬間、FSOは完全に沈黙する。無理やり読み込ませれば文字化けの嵐、書き出せば予期せぬBOMの付与や文字化けによるデータ破損。

業務自動化ツールにおいて、文字化けは「サイレントエラー」を引き起こす最悪のバグだ。
今回は、FSOの限界を突破し、`ADODB.Stream` オブジェクトを組み合わせることで、UTF-8の読み書きを完全に制御するプロダクションコードを授けよう。

—

1. なぜFSO単体ではUTF-8を扱えないのか?

FSOの設計思想は、1990年代後半のWindows環境(Shift_JIS全盛期)で止まっている。
`TextStream` オブジェクトがサポートする文字コード形式は以下の3つのみだ。

1. SystemDefault(システムのデフォルト文字コード:実質Shift_JIS)
2. Unicode(UTF-16LE)
3. ASCII

UTF-8(UTF-8N:BOMなしUTF-8)を直接指定するパラメータは存在しない。
BOM付きUTF-8であれば、Unicode指定で誤魔化せるケースもあるが、外部システム(Linuxサーバー、JSONベースのWeb API、Git管理下にあるファイルなど)との連携において、BOM付きファイルは「ゴミデータが混入している」とみなされ、パースエラーの原因になる。

ここで登場するのが、Windowsのデータアクセスコンポーネントの一部である `ADODB.Stream` だ。

—

2. ADODB.Stream による「文字コードの完全掌握」

`ADODB.Stream` は、本来はデータベースのバイナリデータを扱うためのオブジェクトだが、「文字コード変換機能を持つ高機能なメモリバッファ」として極めて優秀な働きをする。

これを利用することで、以下のメリットが生まれる。

  • 文字コード(`Charset`)に `”UTF-8″` を明示的に指定できる。
  • BOMの有無を完全に制御できる。
  • 読み込み時の文字化け(デコードエラー)を防ぎ、書き込み時の文字ロスをゼロにする。

—

3. 【実装】プロダクションコード:堅牢なUTF-8入出力クラス

それでは、実際の現場でそのままコピー&ペーストして使える、実用的なVBScriptのコードを提示する。
エラーハンドリング(`On Error Resume Next` の適切なスコープ管理)と、オブジェクトの解放(メモリリーク防止)を徹底した、プロフェッショナル仕様だ。

Option Explicit

‘ ==============================================================================
‘ スクリプト名: Utf8IoHelper.vbs
‘ 概要: FSOの制限を回避し、UTF-8(BOMなし/あり)のファイルを安全に読み書きする
‘ ==============================================================================

Dim fileHandler
Set fileHandler = New Utf8IoManager

Dim targetPath, readContent, writeContent
targetPath = “C:\Automation\data\sample.json”

‘ — 1. 書き込みテスト(UTF-8 BOMなし) —
writeContent = “{ “”status””: “”success””, “”message””: “”VBScriptからの安全な出力テスト(日本語含む)”” }”

If fileHandler.WriteTextFile(targetPath, writeContent, False) Then
WScript.Echo “ファイルの書き込みに成功しました。”
Else
WScript.Echo “書き込みエラー: ” & Err.Description
End If

‘ — 2. 読み込みテスト —
readContent = fileHandler.ReadTextFile(targetPath)

If Not readContent = “” Then
WScript.Echo “— 読み込み結果 —”
WScript.Echo readContent
Else
WScript.Echo “読み込みエラーまたはファイルが空です。”
End If

Set fileHandler = Nothing

‘ ==============================================================================
‘ クラス名: Utf8IoManager
‘ 依存関係: ADODB.Stream
‘ ==============================================================================
Class Utf8IoManager

‘ ————————————————————————–
‘ メソッド: ReadTextFile
‘ 概要: 指定されたUTF-8ファイルを確実に読み込む
‘ ————————————————————————–
Public Function ReadTextFile(ByVal filePath)
Dim objStream
Dim fileSystem

‘ FSOでファイルの存在確認
Set fileSystem = CreateObject(“Scripting.FileSystemObject”)
If Not fileSystem.FileExists(filePath) Then
ReadTextFile = “”
Exit Function
End If
Set fileSystem = Nothing

On Error Resume Next
Set objStream = CreateObject(“ADODB.Stream”)

If Err.Number <> 0 Then
ReadTextFile = “”
Exit Function
End If

With objStream
.Type = 2 ‘ adTypeText (テキストデータとして扱う)
.Charset = “UTF-8” ‘ 文字コードをUTF-8に指定
.Open
.LoadFromFile filePath

‘ ストリームのデータを文字列として取得
ReadTextFile = .ReadText(-1) ‘ adReadAll (-1)

.Close
End With

Set objStream = Nothing

If Err.Number <> 0 Then
‘ ログ出力基盤等があればここに記述
ReadTextFile = “”
End If
On Error GoTo 0
End Function

‘ ————————————————————————–
‘ メソッド: WriteTextFile
‘ 概要: 文字列をUTF-8でファイルに書き込む(withBOMがFalseならBOMなし)
‘ ————————————————————————–
Public Function WriteTextFile(ByVal filePath, ByVal content, ByVal withBOM)
Dim objStream
Dim binaryStream

On Error Resume Next
Set objStream = CreateObject(“ADODB.Stream”)

If Err.Number <> 0 Then
WriteTextFile = False
Exit Function
End If

‘ 1. テキストとして書き込み
With objStream
.Type = 2 ‘ adTypeText
.Charset = “UTF-8”
.Open
.WriteText content

‘ BOMの制御が必要な場合の処理
If Not withBOM Then
‘ ADODB.StreamはデフォルトでUTF-8のBOMを付与する。
‘ BOMなしにするため、バイナリに変換して先頭3バイト(BOM)をカットする
Set binaryStream = CreateObject(“ADODB.Stream”)
binaryStream.Type = 1 ‘ adTypeBinary
binaryStream.Open

.Position = 0
.Type = 1 ‘ adTypeBinary 一時的にバイナリモードに切り替え
.CopyTo binaryStream
.Close

‘ バイナリモードで先頭の3バイト(EF BB BF)をスキップして保存
binaryStream.Position = 3

Dim buffer
buffer = binaryStream.Read
binaryStream.Close
Set binaryStream = Nothing

‘ 再度バイナリとしてファイルに書き出す
Set binaryStream = CreateObject(“ADODB.Stream”)
binaryStream.Type = 1
binaryStream.Open
binaryStream.Write buffer
binaryStream.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite
binaryStream.Close
Set binaryStream = Nothing

Else
‘ BOM付きの場合はそのまま保存
.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite
.Close
End If
End With

Set objStream = Nothing

If Err.Number <> 0 Then
WriteTextFile = False
Else
WriteTextFile = True
End If
On Error GoTo 0
End Function

End Class

—

4. チーフアーキテクトからの実践的アドバイス:設計の急所

このコードを現場に導入するにあたり、以下の「エンジニアの知見」を心に留めておいてほしい。

① なぜBOMなし(UTF-8N)の処理をわざわざ入れているのか?

`ADODB.Stream` は、`Charset = “UTF-8″` でファイルセーブを行うと、強制的にBOM(Byte Order Mark: `EF BB BF`)をファイルの先頭に付与する仕様になっている。
これが原因で、JSONパーサーや特定のCLIツールが「先頭に変な文字(`` など)が入っている」と判定してパースエラーを起こす事故が後を絶たない。
上記の `WriteTextFile` メソッド内では、一度バイナリに変換し、ストリームの位置を `3` にずらすことでBOMを完全に削ぎ落とすテクニックを実装している。この一手間が、システム間の連携トラブルを未然に防ぐ防壁となる。

② オブジェクトのライフサイクルとメモリ管理

VBScriptのガベージコレクションは頼りにならない。COMコンポーネント(特に `ADODB.Stream`)は、インスタンスを生成して破棄するサイクルの中でメモリリークを引き起こしやすい。
プロセスが常駐するようなタスクや、数千ファイルのバッチ処理を行う場合は、ループ内でインスタンスを生成・破棄するのではなく、適切にスコープを区切り、`Set objStream = Nothing` を確実に実行してメモリ解放を促すこと。

③ エラーハンドリングの重要性

ファイルI/Oは「外部要因(ネットワークドライブの切断、ファイルロック、権限不足)」で必ず失敗する。
`On Error Resume Next` でエラーを握りつぶすのではなく、必ず `Err.Number` を評価し、失敗時には上位のプロセスへ明確なステータス(`False` や空文字)を返す設計を厳守すること。

—

総括

VBScriptは古い言語かもしれないが、Windows環境における「ちょっとした自動化の歯車」としては今なお強力な武器だ。
FSOの文字コードの呪縛を `ADODB.Stream` で解き放つことで、モダンなWeb APIや外部システムとも互換性のある堅牢なツールチェーンを構築できる。

仕様の壁にぶつかった時、「できない」と諦めるのではなく、背後にあるCOMオブジェクトの仕様を理解し、組み合わせる。それこそが、真の業務自動化エンジニアの流儀である。

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