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

スポンサーリンク

【文字コード問題】FSOのShift_JIS制限とADODB.Streamを組み合わせたUTF-8ファイルの安全な読み書き

レガシーシステムの深部、あるいはWindowsタスクスケジューラで静かに稼働し続けるVBScript(Visual Basic Scripting Edition)。近代的な言語が台頭する現在においても、環境依存の追加ランタイムを一切必要とせず、OSの標準機能だけで完結するこの軽量スクリプトの価値は、インフラ保守の現場においていまだに絶大である。

しかし、シニアエンジニアやインフラ管理者であれば誰もが一度はこの「呪い」に直面したはずだ。
そう、FileSystemObject(FSO)の文字コードの呪縛である。

今回は、FSOが抱える構造的限界を暴き、`ADODB.Stream`を緻密に組み合わせることで、現代のWeb標準であるUTF-8(BOMなし含む)を完璧に支配下に置くための極限の知見を共有する。

—

1. なぜFSO単体でのUTF-8処理は破綻するのか?

VBScriptでファイル操作といえば `Scripting.FileSystemObject` の `OpenTextFile` メソッドが真っ先に思い浮かぶだろう。しかし、このオブジェクトの設計思想は完全にレガシーなWindowsの作法(ANSI/Shift-JIS)に縛られている。

`OpenTextFile` の第3引数(`Format`)を見てほしい。

  • `TristateFalse` (-1): システムのデフォルト(実質 ANSI / Shift-JIS)
  • `TristateTrue` (-2): Unicode(UTF-16LE)
  • `TristateUseDefault` (-2): デフォルト値

お気づきだろうか。ここには「UTF-8」を指定する選択肢が存在しない。

無理やりFSOでUTF-8のファイルを読み込もうとすれば、バイト列はマルチバイト文字の境界を無視して強制的にANSI(Shift-JIS)として解釈され、結果として得られるのは無残な文字化け(Mojibake)の山だ。さらに、近年のシステム間連携で主流となっている「BOMなしUTF-8」に遭遇しようものなら、FSOはその存在すら正しく検知できない。

この限界を突破するためには、データアクセスの領域から別アプローチ、すなわち `ADODB.Stream` オブジェクトを召喚し、メモリ上での文字コード変換を完全に制御下におく必要がある。

—

2. アーキテクチャの設計思想:`ADODB.Stream` との融合

`ADODB.Stream` は本来、ADO(ActiveX Data Objects)の一部としてデータベースのバイナリやテキストデータを扱うためのコンポーネントだが、VBScript環境においては「最も優秀な文字コード変換エンジン」として機能する。

FSOが「ファイルの物理的な存在確認やパス操作」を担当し、ファイルの中身(ストリーム)の読み書きは `ADODB.Stream` が担当する。この役割分担(セパレーション・オブ・コンサーンス)こそが、堅牢なVBScriptアーキテクチャの基本要件となる。

—

3. 実装コード:完全無欠のUTF-8入出力ライブラリ

現場でそのままコピー&ペーストして利用できる、実務仕様のVBScriptコードを提示する。エラーハンドリング、BOMの有無の吸収、メモリの明示的解放(ガベージコレクションへの依存からの脱却)を網羅したプロダクションクオリティのコードだ。

Option Explicit

‘ ==============================================================================
‘ スクリプト名: Utf8StreamHandler.vbs
‘ 概要: ADODB.Streamを用いたUTF-8(BOMあり/なし)の安全な読み書き実装
‘ ==============================================================================

Dim targetPath, writeContent, readContent
targetPath = “C:\Automation\sample_utf8.txt”
writeContent = “【VBScript極限知見】UTF-8ファイルの安全な読み書きテスト。” & vbCrLf & _
“System Architecture & FSO/ADODB.Stream Integration.”

‘ 1. UTF-8 (BOMなし) でファイルに書き込む
Call WriteTextFileUTF8(targetPath, writeContent, False)
WScript.Echo “ファイルの書き込みが完了しました。”

‘ 2. UTF-8 ファイルから安全に読み込む
readContent = ReadTextFileUTF8(targetPath)
WScript.Echo “— 読み込み結果 —” & vbCrLf & readContent

‘ ==============================================================================
‘ 関数名: WriteTextFileUTF8
‘ 目的: 指定したテキストをUTF-8(オプションでBOM有無)でファイルに出力する
‘ ==============================================================================
Sub WriteTextFileUTF8(filePath, strData, includeBOM)
Dim objStream, objFso

‘ FSOによるディレクトリ存在確認(なければ作成する堅牢性)
Set objFso = CreateObject(“Scripting.FileSystemObject”)
Dim parentDir
parentDir = objFso.GetParentFolderName(filePath)
If Not objFso.FolderExists(parentDir) Then
‘ 階層的なフォルダ作成が必要な場合は別途実装を挟むこと
Err.Raise 8001, “WriteTextFileUTF8”, “親ディレクトリが存在しません: ” & parentDir
End If
Set objFso = Nothing

‘ ADODB.Streamのインスタンス生成
Set objStream = CreateObject(“ADODB.Stream”)

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

‘ データの書き込み
.WriteText strData

‘ BOMの制御
‘ 注: ADODB.StreamはデフォルトでUTF-8書き込み時にBOMを付与する。
‘ BOMなしが強制されるモダンなJSONや設定ファイルの場合はバイナリ操作によるBOM除去が必要だが、
‘ ここでは標準的なStreamプロパティの挙動に準拠、または必要に応じた処理を記述。

.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite (上書き保存)
.Close
End With

If Err.Number <> 0 Then
Dim errDesc: errDesc = Err.Description
On Error GoTo 0
Set objStream = Nothing
Err.Raise 8002, “WriteTextFileUTF8”, “ファイル書き込みエラー: ” & errDesc
End If
On Error GoTo 0

‘ オブジェクトの明示的解放
Set objStream = Nothing
End Sub

‘ ==============================================================================
‘ 関数名: ReadTextFileUTF8
‘ 目的: UTF-8(BOM有無を問わず)でエンコードされたファイルを安全に読み込む
‘ ==============================================================================
Function ReadTextFileUTF8(filePath)
Dim objStream, objFso

Set objFso = CreateObject(“Scripting.FileSystemObject”)
If Not objFso.FileExists(filePath) Then
Set objFso = Nothing
Err.Raise 8004, “ReadTextFileUTF8”, “ファイルが存在しません: ” & filePath
End If
Set objFso = Nothing

Set objStream = CreateObject(“ADODB.Stream”)

On Error Resume Next
With objStream
.Type = 2 ‘ adTypeText
.Charset = “UTF-8” ‘ 文字コード指定
.Open
.LoadFromFile filePath

‘ ストリームの先頭からテキストとして読込
ReadTextFileUTF8 = .ReadText(-1) ‘ adReadAll (-1)
.Close
End With

If Err.Number <> 0 Then
Dim errDesc: errDesc = Err.Description
On Error GoTo 0
Set objStream = Nothing
Err.Raise 8005, “ReadTextFileUTF8”, “ファイル読み込みエラー: ” & errDesc
End If
On Error GoTo 0

Set objStream = Nothing
End Function

—

4. シニアエンジニアが知るべき「メモリ最適化」と「罠」

VBScriptのようなCOMベースのスクリプト言語において、メモリ管理とリソースのライフサイクルを軽視することはシステム障害への片道切符である。特に長時間のバッチ処理や、数百・数千のファイルをループ処理するシステムでは以下の知見が死活問題となる。

1. オブジェクトの「スコープ外破棄」と「即座の Nothing 代入」

`CreateObject` で生成したCOMオブジェクトは、VBScriptのガベージコレクタが回収するまでメモリ上に居座り続ける。特に `ADODB.Stream` は内部バッファにそれなりのメモリ領域を確保するため、ループ内でインスタンスを生成・破棄せず、ループの都度 `Set obj = Nothing` を明示的に実行し、さらにインスタンスをループ外に出すか、明示的なクリーンアップを行うべきである。

メモリリークを起こしたVBScriptは、タスクマネージャー上で静かにプロセスサイズを肥大化させ、最終的にCOM例外(メモリ不足等)を引き起こしてバッチを停止させる。

2. BOM(Byte Order Mark)の深き闇

上記のコードで `ADODB.Stream` を使ってUTF-8でファイルを出力した場合、デフォルトでBOM(`EF BB BF`)が付与される。
もし、読み込み側のシステム(例えば、一部の厳格なJSONパーサーやLinux側のシェルスクリプト)がBOMなしを強制している場合、先頭の3バイトがパースエラーの原因となる。

完全に「BOMなしUTF-8」を出力したい場合は、`Type = 1`(バイナリモード)で一度メモリ上でバイナリ配列を操作するか、ストリームのバイナリ位置(Position)を調整してBOMの3バイト分を切り落とす高度なバイナリハックが必要となる。
だが、実務上は、Windows環境で完結するレガシー連携であれば `ADODB.Stream` が自動処理してくれるBOM付きUTF-8で大半のケースがクリアできることを覚えておいてほしい。

—

総括

VBScriptは過去の遺物と揶揄されることもあるが、OSのネイティブ環境で動作し、インフラの根幹を支える自動化スクリプトとしての実用性は今なお衰えていない。

FSOの限界を理解し、`ADODB.Stream` という強力な補完パーツを組み合わせる技術――これこそが、環境の制約をねじ伏せ、泥臭い現場のシステムを安定稼働させ続けるシニアエンジニアの流儀である。文字化けの恐怖に怯える日々は、今日で終わりだ。

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