【入門編】【テキスト差分検出】2つの設定ファイルやログの差分(Diff)を比較・抽出するアルゴリズムの実装 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!業務自動化の現場を駆け抜けてきた先輩エンジニアです。

マクロの記録や簡単な自動化から一歩進んで、「自前のしっかりとしたツールを作りたい」と思ったことはありませんか?
今回は、Windows環境であれば追加のインストールなしでどこでも動くVBScript(Visual Basic Scripting Edition)とWSH(Windows Script Host)を使って、「2つのテキストファイルの差分(Diff)を検出する軽量モジュール」を一緒に作ってみましょう。

「外部のDiffツールが入れられない閉じた環境で、設定ファイルの変更履歴をサクッと比較したい!」
そんな現場の切実な要望をスマートに解決する、プログラミングの本質が詰まったコードを解説します。ここをクリアすれば、VBScriptの基礎はバッチリですよ!

なぜVBScriptで「Diff(差分検出)」なのか?

業務システムやサーバーの運用現場では、次のような悩みがつきものです。

  • 「exeや外部ツールを持ち込むセキュリティ上の許可が下りない」
  • 「PythonやPowerShellを入れたいが、古いWindows Server環境で動かない」

こんなとき、どのWindows端末にも標準搭載されているVBScript(`.vbs`)の出番です。
今回は、配列と連想配列(Dictionary)を巧みに操り、2つのテキストファイルの「行単位での追加・削除・一致」を完全に暴き出すアルゴリズムを実装します。

差分検出の全体像(アルゴリズムのキホン)

2つのファイルを比較するとき、単純に「1行目同士、2行目同士…」と比べても、途中で1行追加されただけで、それ以降の行がすべて「変更された」と判定されてしまいますよね。

そこで、今回の軽量Diffモジュールでは以下のステップで処理を行います。

1. ファイル読み込み: 旧ファイル(Old)と新ファイル(New)をそれぞれ配列(行単位)としてメモリ上に展開する。
2. 存在チェック(Dictionaryの活用): 「新ファイルの各行が、旧ファイルのどこに存在するか」をハッシュマップ(`Scripting.Dictionary`)で高速にインデックス化する。
3. 差分判定:

  • 削除 (Deleted): 旧ファイルにあって、新ファイルにない行。
  • 追加 (Added): 新ファイルにあって、旧ファイルにない行。
  • 一致 (Equal): 両方に存在する行。

これをVBScriptのコードに落とし込んでいきましょう。

実装コード:軽量Diff検出スクリプト

以下のコードをメモ帳に貼り付け、拡張子を `.vbs` (例: `text_diff.vbs`)として保存してください。

‘ =================================================================0
‘ 2ファイル行単位 差分検出スクリプト (TextDiff.vbs)
‘ 対象: 設定ファイルやログの簡易Diffレポート生成
‘ =================================================================Option Explicit

Dim fso, objArgs
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set objArgs = WScript.Arguments

‘ 引数チェック
If objArgs.Count < 2 Then WScript.Echo "【使い方】" & vbCrLf & _ "cscript text_diff.vbs <旧ファイルパス> <新ファイルパス>”
WScript.Quit(1)
End If

Dim oldPath, newPath
oldPath = objArgs(0)
newPath = objArgs(1)

‘ ファイルの存在確認
If Not fso.FileExists(oldPath) Then
WScript.Echo “エラー: 旧ファイルが見つかりません -> ” & oldPath
WScript.Quit(1)
End If
If Not fso.FileExists(newPath) Then
WScript.Echo “エラー: 新ファイルが見つかりません -> ” & newPath
WScript.Quit(1)
End If

‘ メイン処理の実行
Call GenerateDiffReport(oldPath, newPath)

‘ —————————————————————–
‘ 差分レポート生成メイン関数
‘ —————————————————————–
Sub GenerateDiffReport(pathA, pathB)
Dim fileA, fileB, textA, textB
Dim arrA, arrB

‘ ファイルをテキストとして一括読み込み (UTF-8/Shift-JIS等の文字コードに注意)
‘ ※今回は標準的なテキスト読み込み(ForReading = 1)を使用
Const ForReading = 1
Set fileA = fso.OpenTextFile(pathA, ForReading, False)
Set fileB = fso.OpenTextFile(pathB, ForReading, False)

textA = fileA.ReadAll
textB = fileB.ReadAll

fileA.Close
fileB.Close

‘ 改行コード(CRLF / LF)を統一して配列に分割
textA = Replace(textA, vbCrLf, vbLf)
textA = Replace(textA, vbCr, vbLf)
arrA = Split(textA, vbLf)

textB = Replace(textB, vbCrLf, vbLf)
textB = Replace(textB, vbCr, vbLf)
arrB = Split(textB, vbLf)

WScript.Echo “========================================”
WScript.Echo ” 差分解析結果レポート”
WScript.Echo ” 比較元: ” & pathA
WScript.Echo ” 比較先: ” & pathB
WScript.Echo “========================================” & vbCrLf

‘ 連想配列を使って旧ファイルの行ハッシュを作成 (高速検索のため)
Dim dictA
Set dictA = CreateObject(“Scripting.Dictionary”)

Dim i
For i = 0 To UBound(arrA)
‘ キーが存在しない場合のみ追加(重複行の考慮)
If Not dictA.Exists(arrA(i)) Then
dictA.Add arrA(i), True
End If
Next

‘ 新ファイルをベースに「追加」と「一致」を判定
Dim dictB
Set dictB = CreateObject(“Scripting.Dictionary”)
For i = 0 To UBound(arrB)
dictB.Add arrB(i), True

If Not dictA.Exists(arrB(i)) Then
WScript.Echo “[+] 追加: ” & arrB(i)
Else
‘ WScript.Echo “[=] 一致: ” & arrB(i) ‘ 詳細すぎる場合はコメントアウト
End If
Next

‘ 旧ファイルをベースに「削除」を判定
For i = 0 To UBound(arrA)
If Not dictB.Exists(arrA(i)) Then
WScript.Echo “[-] 削除: ” & arrA(i)
End If
Next

WScript.Echo vbCrLf & “========================================”
WScript.Echo ” 処理が完了しました。”
WScript.Echo “========================================”
End Sub

コードの重要なポイントとVBScriptの作法

ここからは、なぜこのコードが効率的で実用的なのか、エンジニアの視点で重要なポイントを解説します。

1. `Option Explicit` はプロフェッショナルの証

コードの先頭にある `Option Explicit`。これ、絶対に省略してはいけません。
VBScriptはデフォルトでは変数の宣言なしで使うことができますが、これだとタイポ(入力ミス)したときに変なバグの原因になります。強制的に変数宣言を義務付けることで、保守性の高いコードになります。

2. `Scripting.Dictionary` による超高速検索

今回のアルゴリズムの肝は、`Scripting.Dictionary` オブジェクトです。
もし、新ファイルの各行が旧ファイルに含まれているかを調べるために、配列を毎回頭からしらみつぶしに検索(線形探索)していたらどうでしょう?
行数が10,000行を超えたあたりから処理が急激に重くなります(計算量 $O(N^2)$)。

Dictionaryオブジェクトを使うことで、ハッシュ値ベースの高速なキー検索 $O(1)$ が可能になり、巨大なログファイルや設定ファイルであっても一瞬で差分を抽出できます。

3. 改行コードの罠への対策

Windowsの改行 (`vbCrLf`) と、LinuxやMacの改行 (`vbLf`) が混在しているテキストをそのまま `Split` すると、意図しない空行が生まれて誤検知の原因になります。
スクリプト内で `Replace` を使って一旦すべて `vbLf` に統一してから配列化しているのは、実務で絶対に押さえておきたい「泥臭いけれど確実なテクニック」です。

陥りやすいエラーと注意点

初心者がVBScriptでファイル操作を行う際、よくつまずくポイントを挙げておきます。

  • 文字コードのエラー(文字化け)
  • VBScriptの `OpenTextFile` は、デフォルトのエンコーディングがOSの言語設定(日本語版ならShift-JIS)に依存します。
  • 近年のUTF-8(BOMなし)のファイルを読み込むと文字化けや比較ミスが起きるため、事前にメモ帳などで「Shift-JIS (ANSI)」に変換するか、ADODB.Streamオブジェクトを使った高度な読み込みに拡張する必要があります。
  • コマンドプロンプト(cscript)からの実行
  • デフォルトの `wscript.exe` で実行すると、結果がポップアップのダイアログボックスで大量に出てしまい非常に見づらくなります。
  • 実行する際は、必ずコマンドプロンプトから `cscript text_diff.vbs old.txt new.txt` のように `cscript` を明示して実行してください。

おわりに

いかがでしたでしょうか?
今回は、VBScriptを使って外部ツールに頼らずにテキストの差分を検出する軽量モジュールを作成しました。

「古い言語だから…」と敬遠されがちなVBScriptですが、WSHと組み合わせることで、Windows環境における最強の「即席オートメーション武器庫」になります。この構造を理解できれば、VBAでのデータ突合や、PowerShellへのステップアップも驚くほどスムーズになるはずです。

日々の地道な業務をスマートに自動化して、自分の時間を取り戻していきましょう!それでは、また次のテクニカルな現場でお会いしましょう。

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