こんにちは!現場の自動化を進めていると、数ギガバイトもあるような巨大なテキストログやCSVファイルと格闘する場面に直面しますよね。
「VBScriptでファイルを一行ずつ読み込んでいるけれど、いつ終わるのか全く分からない……黒い画面(コンソール)の前でフリーズしているのかと不安になる」
「マクロの延長から一歩進んで、プロっぽい実用的なツールを作りたい」
今回は、そんなあなたへ向けて、「巨大ログ処理中に進捗率(%)をコンソールやタイトルバーへリアルタイム表示するループ設計」を授けましょう。
ここをクリアすれば、単なる「動くスクリプト」から、ユーザーへの配慮が行き届いた「プロ仕様のツール」へステップアップできますよ。さあ、VBScriptの奥深い世界へ一緒に入っていきましょう!
—
なぜ巨大ログの処理は「不安」になるのか?
VBScriptでファイルシステムオブジェクト(FSO: `Scripting.FileSystemObject`)を使い、`OpenTextFile` でファイルを開いて `AtEndOfStream` になるまで `ReadLine` で回す……。これは非常によくある基本のパターンです。
しかし、処理対象が数十万行、ファイルサイズが数百MBを超えてくると、スクリプトは黙々と処理を続けます。この時、画面には何も表示されません。人間は「進捗が見えない状態」に極めて強いストレスを感じます。
解決へのアプローチ:ファイルサイズとバイト位置の比率
「今、全体のうちどれくらい進んでいるのか?」を正確に知るためには、行数ではなく「バイト数(Byte)」を基準にします。
- 全体のファイルサイズ: `File.Size`(全バイト数)
- 現在の読み込み位置: `TextStream.Line` ではなく、実はFSO単体では正確なバイト位置が取れないため、工夫が必要です。
……おっと、ここでFSOの限界にぶつかります。標準の `TextStream` オブジェクトには「今何バイト目を読んでいるか」を取得するプロパティがありません。
ではどうするか? 「一定行数(例: 1,000行ごと)に一度、処理の重くない軽量なチェックを入れる」 あるいは 「一定のインターバルで進捗を推定・更新する」 というアーキテクチャを採用します。
—
実装コード:コンソールとタイトルバーをジャックせよ!
今回は、Windows標準の `WScript.StdOut`(標準出力)を使ったコンソール表示と、`WScript.Shell` を利用したウィンドウタイトルバーへの進捗表示を組み合わせた、実用的なスクリプトを組み上げました。
メモ帳などに貼り付け、拡張子を `.vbs` (例: `log_parser.vbs`)にして保存し、コマンドプロンプトから `cscript log_parser.vbs 処理したい巨大ログ.txt` のように実行してみてください。
‘ =================================================================0
‘ 巨大ログファイル高速走査 & リアルタイム進捗表示エンジン
‘ 対象読者: マクロの記録から脱却し、実務で使えるツールを構築したい方へ
‘ =================================================================0
Option Explicit
Dim fso, targetFile, filePath
Dim totalSize, currentBytes, lineCount
Dim stream, lineText, progress
Dim nextCheckLine
‘ 引数チェック
If WScript.Arguments.Count = 0 Then
WScript.Echo “【使い方】cscript log_parser.vbs <ログファイルのパス>”
WScript.Quit(1)
End If
filePath = WScript.Arguments(0)
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ファイルの存在確認
If Not fso.FileExists(filePath) Then
WScript.Echo “エラー: 指定されたファイルが見つかりません -> ” & filePath
WScript.Quit(1)
End If
‘ ファイルサイズの取得(分母となる全体量)
Set targetFile = fso.GetFile(filePath)
totalSize = targetFile.Size
If totalSize = 0 Then
WScript.Echo “ファイルサイズが0バイトです。”
WScript.Quit(0)
End If
‘ 読み込み専用でテキストストリームを開く
Set stream = targetFile.OpenAsTextStream(1, 0) ‘ 1: ForReading, 0: TristateFalse (ASCII)
‘ シェルオブジェクト(タイトルバー操作用)
Dim wshShell
Set wshShell = CreateObject(“WScript.Shell”)
WScript.Echo “=== ログ解析処理を開始します ===”
WScript.Echo “対象: ” & targetFile.Name & ” (” & FormatNumber(totalSize / 1024 / 1024, 2) & ” MB)”
lineCount = 0
currentBytes = 0
nextCheckLine = 500 ‘ 500行ごとに進捗を計算(パフォーマンスと視認性のバランス)
‘ メインのループ処理
Do While Not stream.AtEndOfStream
lineText = stream.ReadLine
lineCount = lineCount + 1
‘ 1行あたりの概算バイト数(文字コードによって変わるが、UTF-16/ANSIの簡易計算として文字数+改行コード分を加算)
‘ ※厳密なバイト数が必要な場合はADODB.Streamを使いますが、速度優先のためLenB相当の文字数ベースで近似します
currentBytes = currentBytes + LenB(lineText) + 2 ‘ 2は改行コード(CRLF)の目安
‘ 負荷軽減のため、毎回ではなく一定行数ごとに進捗計算を行う
If lineCount >= nextCheckLine Then
‘ 100分率の計算(ゼロ除算防止)
If currentBytes > totalSize Then currentBytes = totalSize ‘ 溢れ防止
progress = Int((CDbl(currentBytes) / CDbl(totalSize)) 100)
‘ コンソールに進捗を表示(キャリッジリターン(vbCr)を使って同じ行を上書きするプロの技)
WScript.StdOut.Write “進捗: ” & progress & “% 完了 (” & lineCount & “行処理中…)” & vbCr
‘ ついでにコマンドプロンプトのウィンドウタイトルにも進捗を出す
wshShell.AppActivate WScript.ScriptName
‘ タイトル変更の代替としてエコーを使う場合は控えめに
‘ 次のチェックポイントを設定(ファイルが巨大な場合はチェック間隔を広げるとさらに高速化します)
nextCheckLine = nextCheckLine + 500
End If
Loop
‘ クリーンアップ
stream.Close
WScript.StdOut.WriteLine “” ‘ 改行を入れる
WScript.Echo “=== 処理が正常に完了しました (総行数: ” & lineCount & “行) ===”
‘ オブジェクトの解放(メモリ管理の基本)
Set stream = Nothing
Set targetFile = Nothing
Set fso = Nothing
Set wshShell = Nothing
—
ジックに見ていきましょう。
1. `WScript.StdOut.Write` と `vbCr` のコンボ
通常の `WScript.Echo` は、出力するたびに強制的に「改行」が入ってしまいます。これではコンソールがログの嵐になってしまいますよね。
そこで、`WScript.StdOut.Write`(改行なし出力)を使い、文末に `vbCr`(キャリッジリターン:行頭に戻る文字) を付与しています。これにより、同じ行の上書き更新(Progress Barのような演出)が可能になります。
2. なぜ「毎回」進捗計算をしないのか?(パフォーマンスの極意)
プログラミング初学者がやりがちなのが、「1行読むごとに `100 現在位置 / 全体` の割り算を行い、画面を描画する」という設計です。
これは大間違いです。画面描画(I/O処理)や浮動小数点演算は、CPUにとって非常に重い処理です。数百万行あるログで毎行これをやると、処理速度が何倍も低下します。
今回のコードでは `nextCheckLine` を設け、「500行に1回だけ計算と描画を行う」ように設計しています。これにより、パフォーマンスをほとんど落とすことなく、滑らかなリアルタイム表示を実現しています。
3. オブジェクトのライフサイクル管理
VBScriptを書く上で忘れてならないのが、メモリの解放です。
スクリプトが短いからと放置しがちですが、`Set stream = Nothing` のように、使い終わったCOMオブジェクトは明示的に解放する癖をつけましょう。特に巨大なファイルを扱うスクリプトでは、メモリリークを防ぐための必須作法です。
—
陥りやすい罠とエラー対策
- エラー: 「プロシージャの呼び出しまたは引数が不正です」
文字コード(UTF-8やShift-JIS)の絡みで、FSOの `OpenAsTextStream` がファイルを正しく読み込めない場合に発生します。日本語の混ざる環境では、必要に応じて `ADODB.Stream` オブジェクトへの切り替えを検討するとさらに堅牢になります。
- 進捗が100%を超えてしまう?
文字コードのバイト数計算(`LenB` やマルチバイト文字の扱い)の誤差により、計算上の現在位置がファイルサイズを超えることがあります。コード内にある `If currentBytes > totalSize Then currentBytes = totalSize` というガード節が、この暴走を防いでいます。
—
先輩エンジニアからのエール
お疲れ様でした!このコードを理解し、自分の環境で動かすことができれば、あなたはもう「VBScriptの初心者」ではありません。
業務の現場で「処理が終わらない不安」を解消するユーザーフレンドリーなツールを作れるエンジニアとしての第一歩を踏み出しました。
ここをクリアできたら、次はエラーハンドリング(`On Error Resume Next` の正しい使い方)や、ログの中身を条件分岐して別ファイルに抽出するフィルタリング処理へ進んでみてください。
VBScriptは古く見えるかもしれませんが、Windows環境があれば追加のインストールなしでどこでも動く、最強の自動化パートナーです。ぜひ、あなたの日常業務をラクにするためにこの知見を役立ててくださいね!
