VBScriptを極める:BOMなしUTF-8の「外科手術的」結合術
Windowsの自動化において、VBScriptはもはや「レガシー」というレッテルを貼られがちだ。しかし、真のアーキテクトは知っている。OSの深層に根ざしたWSH(Windows Script Host)の実行環境こそ、軽量かつ高速なインフラ連携の要であることを。
特に、データ連携の現場で頻出する「BOMなしUTF-8」のハンドリング。標準の `FileSystemObject` (FSO) はUTF-16 LEに固執し、BOMを付与する。これを力技で結合すれば、中間ファイルにBOMが混入し、後続のシステム(Linux環境やクラウドAPI)がエラーを吐く。
今日は、`ADODB.Stream` をバイナリレイヤーで操作し、メモリを浪費せずに大規模ファイルを結合する「外科手術」のようなコードを伝授する。
—
なぜADODB.Streamなのか
`FileSystemObject` を使うのは、テキスト処理の基本だが、ストリーム制御においては無力だ。`ADODB.Stream` は、本来データベースのBLOBを扱うためのものだが、その真価は「バイナリの断片を結合する際の透過性」にある。
UTF-8のBOM(`EF BB BF`)を回避するには、テキストモードを捨て、バイナリとして読み込み、連結するしかない。このアプローチは以下のメリットをもたらす。
1. メモリ効率: 大容量ファイルでもバッファを制御することで、OSへの負荷を最小限に抑える。
2. 整合性: BOMを物理的に除外して読み込むため、後続処理で「予期せぬ文字化け」が起きない。
3. 移植性: Windows標準のコンポーネントのみを使用するため、ライブラリのインストールは一切不要。
—
極限の結合スクリプト:実装と解析
以下は、指定フォルダ内のBOMなしUTF-8ファイルを結合し、単一の出力ファイルへ書き出すための堅牢なスクリプトである。
‘ — 極限のファイル結合スクリプト (ConcatUTF8.vbs) —
Option Explicit
Dim fso, folder, files, file
Dim outStream, inStream
Dim targetFolder, outputFile
targetFolder = “C:\Data\Logs”
outputFile = “C:\Data\Output\Combined.log”
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set outStream = CreateObject(“ADODB.Stream”)
‘ 出力ストリームをバイナリモードで初期化
outStream.Type = 1 ‘ adTypeBinary
outStream.Open
Set folder = fso.GetFolder(targetFolder)
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Name)) = “txt” Then
Set inStream = CreateObject(“ADODB.Stream”)
inStream.Type = 1 ‘ adTypeBinary
inStream.Open
inStream.LoadFromFile file.Path
‘ BOM (3バイト) をスキップするためのシーク処理
If inStream.Size >= 3 Then
inStream.Position = 3
End If
‘ 残りのデータを読み込み、出力ストリームへ追記
outStream.Write inStream.Read
inStream.Close
Set inStream = Nothing
End If
Next
‘ 書き込みとクリーンアップ
outStream.SaveToFile outputFile, 2 ‘ adSaveCreateOverWrite
outStream.Close
Set outStream = Nothing
Set fso = Nothing
—
アーキテクトの視点:コードの深層
このコードには、ただ動くだけではない「意図」が組み込まれている。
1. `inStream.Position = 3` の必然性
UTF-8のBOMは先頭の3バイト `0xEF, 0xBB, 0xBF` で構成される。`Position = 3` とすることで、ストリームのポインタをBOMの直後に強制移動させている。もしファイルにBOMが含まれていない場合は、この処理を条件分岐で制御する必要があるが、現代のシステム連携では「BOMあり/なしが混在する」ケースが多いため、正規化処理を前段に置くのがアーキテクチャの鉄則である。
2. オブジェクトのライフサイクル管理
`Set inStream = Nothing` をループ内で明示的に行っているのは、COMオブジェクトの解放をメモリ管理者に任せないためだ。VBScriptのガベージコレクションは非力であり、数千ファイルの結合を行う際、解放を怠ればメモリリークによるプロセス終了を招く。
3. パフォーマンスの最適化
`outStream.Write inStream.Read` は、メモリ内に全データを展開する。もし結合対象がギガバイト単位に及ぶ場合は、`inStream.Read(chunkSize)` を利用してバッファリングを行う実装に切り替える必要がある。これはエンジニアの腕の見せ所だ。
—
結論:レガシーを「武器」にする
VBScriptは過去の遺物ではない。Windows環境において、特定の権限や設定なしに実行可能な軽量なバイナリ制御ツールとして、今なお最強の選択肢の一つだ。
APIやモダンな言語(Python/Go)に頼るまでもない、数メガバイトのログ処理や、自動化の初動において、この「ADODB.Streamによる直打ち」を使いこなせるかどうか。それが、凡百のスクリプターと、伝説的な自動化エンジニアを分かつ境界線となる。
さあ、目の前の退屈な手作業を、このアーキテクチャで根絶やしにしてほしい。それがエンジニアの矜持というものだ。
