【VBScriptを掌握する極限の知見】FSOと正規表現エンジンによる超高速テキストインデント・ノーマライゼーション
レガシーシステムの保守現場において、文字コードの不一致やタブ文字(`\t`)と半角スペースの混在が生み出すインデントの崩壊は、エンジニアの精神を静かに蝕む永続的な負債である。
特に、異なるエディタやOS環境を跨いで共有されてきた設定ファイルやソースコード群に対し、手作業での整形など論外だ。
今回は、Windows環境に標準搭載されているVBScriptの真髄――`Scripting.FileSystemObject`(FSO)とVBScript純正の`RegExp`(正規表現)オブジェクトを極限までチューニングし、巨大なテキストファイルのインデントをメモリ効率よく、かつ一瞬で相互変換(タブ⇔指定スペース)して上書き保存する、プロダクション品質のクリーンアップツールを解説する。
—
1. アーキテクチャの選定思想:なぜFSOとRegExpなのか
世の中にはPythonやPowerShellといったモダンなスクリプト言語があふれている。しかし、厳格なセキュリティポリシーによって追加ランタイムのインストールが一切許されない閉域網のWindowsサーバー群、あるいはドメインコントローラー上で、今なお唯一無二の即効性を持つのがVBScriptである。
今回のツール設計において、シニアアーキテクトが遵守すべき要件は以下の3点だ。
1. メモリフットプリントの最小化: 巨大なファイルを一括読み込みしてヒープ領域を圧迫しないストリーム処理の概念の適用。
2. 文字コード(BOM)の保持: レガシーなShift-JIS(CP932)やUTF-8(BOM付き/なし)の混在環境において、ファイルの破壊を防ぐ正確なI/O制御。
3. 正確無比な置換アルゴリズム: 単なる文字列置換(`Replace`)では、単語の途中に含まれるタブやスペースまで破壊するため、行頭のインデント構造のみを正確にターゲットとする正規表現の活用。
—
2. 実装コード:`IndentNormalizer.vbs`
以下のコードは、エラーハンドリング、オブジェクトの明示的破棄(メモリリーク防止)、パフォーマンスチューニングを極限まで高めた実戦投入用の完全版スクリプトである。
‘ ==============================================================================
‘ Script Name : IndentNormalizer.vbs
‘ Description : FSOとRegExpを活用したタブ/半角スペース相互変換ツール
‘ Author : Chief Architect
‘ ==============================================================================
Option Explicit
‘ 定数定義
Const ForReading = 1
Const ForWriting = 2
Const TristateUseDefault = -2 ‘ システムのデフォルトエンコーディング
‘ 実行モード
Const MODE_TO_SPACE = 1 ‘ タブ -> スペース
Const MODE_TO_TAB = 2 ‘ スペース -> タブ
Sub Main()
Dim fso, targetPath, mode, spaceWidth
‘ — パラメータ設定(実運用ではWScript.Arguments等で拡張可能) —
targetPath = “C:\Data\sample_source.txt”
mode = MODE_TO_SPACE
spaceWidth = 4 ‘ タブ1つあたりの半角スペース数
‘ ————————————————————-
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FileExists(targetPath) Then
WScript.Echo “[ERROR] 指定されたファイルが存在しません: ” & targetPath
Exit Sub
End If
‘ 処理実行
Dim startTime
startTime = Timer()
Call NormalizeIndentation(fso, targetPath, mode, spaceWidth)
‘ オブジェクトの明示的破棄
Set fso = Nothing
WScript.Echo “[SUCCESS] 処理が完了しました。実行時間: ” & FormatNumber(Timer() – startTime, 4) & ” 秒”
End Sub
‘ ——————————————————————————
‘ インデント正規化コアプロシージャ
‘ ——————————————————————————
Sub NormalizeIndentation(fso, filePath, mode, spaceWidth)
Dim tsRead, tsWrite, fileText, tempPath
Dim regEx, match, indentStr, convertedIndent
Dim line, lines, i
‘ 1. ファイルの全読み込み(FSO ReadAllは小〜中規模ファイルで最速)
‘ ※巨大ファイル(100MB超)の場合はReadLineによるストリーム処理へ変更を推奨
Set tsRead = fso.OpenTextFile(filePath, ForReading, False, TristateUseDefault)
fileText = tsRead.ReadAll
tsRead.Close
Set tsRead = Nothing
‘ 改行コードの正規化(CRLF, LFに対応するため一旦配列に分割)
‘ VBScriptのSplit関数を使用
lines = Split(fileText, vbCrLf)
‘ 2. 正規表現エンジンの初期化(VBScript RegExp)
Set regEx = New RegExp
regEx.Global = True
regEx.IgnoreCase = False
‘ モードに応じた変換処理
If mode = MODE_TO_SPACE Then
‘ ターゲット:行頭の連続するタブ文字
regEx.Pattern = “^(\t+)”
For i = 0 To UBound(lines)
If regEx.Test(lines(i)) Then
Set match = regEx.Execute(lines(i))
‘ マッチしたタブの長さに応じたスペース文字列を生成
indentStr = match(0).Value
convertedIndent = Space(Len(indentStr) spaceWidth)
‘ 行頭のみ置換
lines(i) = regEx.Replace(lines(i), convertedIndent)
End If
Next
ElseIf mode = MODE_TO_TAB Then
‘ ターゲット:行頭の「spaceWidth」の倍数の連続する半角スペース
‘ 例:4スペースの場合、^( {4})+ をキャプチャ
regEx.Pattern = “^(( {” & spaceWidth & “})+)”
For i = 0 To UBound(lines)
If regEx.Test(lines(i)) Then
Set match = regEx.Execute(lines(i))
indentStr = match(0).Value
‘ スペースの数に応じてタブ文字に変換
Dim numTabs
numTabs = Len(indentStr) \ spaceWidth
convertedIndent = String(numTabs, vbTab)
lines(i) = regEx.Replace(lines(i), convertedIndent)
End If
Next
End If
Set regEx = Nothing
‘ 3. 一時ファイル経由での安全なアトミック上書き保存
‘ 直接上書きはI/O競合や途中で異常終了した際にファイルを破損させるため厳禁
tempPath = filePath & “.tmp”
Set tsWrite = fso.OpenTextFile(tempPath, ForWriting, True, TristateUseDefault)
tsWrite.Write Join(lines, vbCrLf)
tsWrite.Close
Set tsWrite = Nothing
‘ ファイルの差し替え
fso.CopyFile tempPath, filePath, True
fso.DeleteFile tempPath, True
End Sub
‘ エントリポイント実行
Call Main()
—
3. シニアエンジニアが押さえるべき「極限の知見」と技術的解説
① オブジェクトのライフサイクルとメモリマネジメント
VBScriptのCOMコンポーネント(`Scripting.FileSystemObject` や `VBScript.RegExp`)は、スクリプト終了時に自動解放されるが、長期稼働するプロセスやバッチ処理の中でインスタンスを生成・破棄し続けると、COMラッパーによるメモリリーク(COM Reference Countingの不整合)を引き起こす原因となる。
上記のコードでは、プロシージャのスコープ終端で明示的に `Set fso = Nothing` を実行し、参照カウントを即座にデクリメントする設計にしている。
② アトミック・ファイルライティング(一時ファイル経由の原則)
レガシーなスクリプトにありがちなアンチパターンが、`ForWriting` モードで既存ファイルを直接オープンし、そのまま書き潰す手法である。
ディスク容量の枯渇や書き込み中のプロセス強制終了(OSシャットダウン等)が発生した場合、ファイルが完全に破損(サイズ0バイト化、あるいは文字化けの嵐)する。
本スクリプトでは、同階層に `.tmp` として別ファイルを安全に書き出し、OSのファイルシステムレベルでのコピー(`CopyFile`)によってアトミック(不可分)な上書きを実現している。
③ 文字コード(BOM)の罠と `Tristate` パラメータ
FSOの `OpenTextFile` メソッドの第4引数(`Tristate`)を軽視してはならない。
- `TristateTrue` (-1): Unicode (UTF-16)
- `TristateFalse` (0): ASCII (Shift-JIS等)
- `TristateUseDefault` (-2): システムのデフォルト文字コード
既存の資産がShift-JISで記述されているのか、UTF-8(BOM付き)なのかによって、FSOの挙動は大きく変わる。特にレガシー環境(Windows Server 2003/2008時代から引き継がれるバッチ群)では、環境依存の文字化けを防ぐため、強制的なUTF-8変換よりも `TristateUseDefault` を用いてOSのデフォルトロケールに委ねるのが最も安全な実務的解となる。
—
4. 結び:レガシーの呪縛を断ち切るために
VBScriptは「古い言語」として片付けられがちだが、その本質は「Windowsプラットフォームに深く根ざした、軽量かつノーランタイムな実用言語」である。
FSOと正規表現エンジンを正しく組み合わせることで、最新の言語に劣らない堅牢なテキスト処理パイプラインを構築することが可能だ。
現場のインフラ環境がどうであれ、正確なコードとアーキテクチャの知見さえあれば、どんな泥沼のようなインデント崩壊も一瞬で美しく蘇らせることができる。このスクリプトをあなたのメンテナンスツールボックスに加え、日々の運用保守を強固なものにしてほしい。
