【極限の知見】FSOを使い倒す:複数ファイルからのキーワード集計とクロス集計CSV出力の極意
「大量のログファイルの中から、特定のキーワードがそれぞれ何回出てくるか調べて……」
そんな依頼を上司から受けたとき、あなたはどうしますか? 一つずつファイルを開いて検索し、Excelに手入力する……? もしそう考えているなら、今日、その習慣に別れを告げましょう。
プロフェッショナルな自動化エンジニアは、「構造化された思考」と「適切なオブジェクトの選択」によって、数時間かかる作業を数秒のスクリプトに置き換えます。
今回は、VBScriptの心臓部とも言える`FileSystemObject` (FSO) を使い、複数のテキストファイルからキーワードの出現頻度を網羅的にスキャンし、最終的に「ファイル名 × キーワード」のクロス集計表(CSV)を生成する、実戦的なツールを構築します。
ここをマスターすれば、あなたは単なる「マクロ使い」から、「業務プロセスを設計するエンジニア」へと脱皮できるはずですよ。
1. 解決したい課題:データの「マトリックス化」
例えば、以下のような状況を想像してください。
- 対象ファイル: `log_01.txt`, `log_02.txt`, `log_03.txt` …
- 探したいキーワード: `ERROR`, `WARNING`, `TIMEOUT`
私たちが欲しいのは、単なる合計数ではなく、「どのファイルに、どのエラーが、いくつ含まれているか」が一目でわかる表です。
| ファイル名 | ERROR | WARNING | TIMEOUT |
|---|---|---|---|
| log_01.txt | 5 | 2 | 0 |
| log_02.txt | 0 | 10 | 1 |
これを手作業でやるのは苦行ですが、VBScriptなら「データの収集」→「集計」→「出力」の3ステップで完結します。
2. 実装コード:プロフェッショナルの設計図
それでは、魂を込めたコードを見ていきましょう。このコードは、単に動くだけでなく、「拡張性」と「メモリ効率」を意識して設計しています。
' ==============================================================================
' ツール名: Keyword Cross-Tabulation Tool (FSO Edition)
' 概要: 指定フォルダ内のテキストファイルをスキャンし、キーワード出現回数を
' クロス集計してCSV出力する。
' ==============================================================================
Option Explicit
' --- 設定エリア ---
Const TARGET_FOLDER = "C:\Logs\Target" ' 解析対象のフォルダパス
Const OUTPUT_CSV = "C:\Logs\Result\analysis_result.csv" ' 出力先CSV
Dim KEYWORDS ' 検索したいキーワードの配列
' 検索したいキーワードを定義(ここを増やすだけで対応可能)
KEYWORDS = Array("ERROR", "WARNING", "TIMEOUT", "CRITICAL")
' --- メイン処理 ---
Main
Sub Main()
Dim fso, folder, file, ts
Dim dictResult, key, filePath, fileName, lineContent
Dim i, count, word
' 1. オブジェクトの生成
' FileSystemObject: ファイル・フォルダ操作の要
' Scripting.Dictionary: 高速なデータ格納・検索のための「連想配列」
Set fso = CreateObject("Scripting.FileSystemObject")
Set dictResult = CreateObject("Scripting.Dictionary")
' フォルダの存在確認
If Not fso.FolderExists(TARGET_FOLDER) Then
MsgBox "エラー: 指定されたフォルダが見つかりません。" & vbCrLf & TARGET_FOLDER, vbCritical
Exit Sub
End If
On Error Resume Next ' ファイルアクセスエラー対策
' 2. ファイルのスキャンループ
Set folder = fso.GetFolder(TARGET_FOLDER)
For Each file In folder.Files
' テキストファイルのみを対象とする(拡張子チェック)
If LCase(fso.GetExtensionName(file.Name)) = "txt" Or LCase(fso.GetExtensionName(file.Name)) = "log" Then
' ファイル名ごとに辞書を作成するための準備
' dictResult(ファイル名) = Dictionary(キーワード -> 回数) という二重構造にする
Set dictResult(file.Name) = CreateObject("Scripting.Dictionary")
' キーワードの初期化(出現回数0でセット)
For Each word In KEYWORDS
dictResult(file.Name)(word) = 0
Next
' ファイルを開いて中身を一行ずつ読み込む
Set ts = file.OpenAsTextStream(1) ' 1 = ForReading
If Err.Number = 0 Then
Do Until ts.AtEndOfStream
lineContent = ts.ReadLine
' 各行に対して、全キーワードをチェック
For Each word In KEYWORDS
' InStrを使って出現回数をカウント(簡易的な実装)
' ※より厳密な単語境界が必要な場合は正規表現が必要だが、ログ解析ならこれで十分
count = CountOccurrences(lineContent, word)
dictResult(file.Name)(word) = dictResult(file.Name)(word) + count
Next
Loop
ts.Close
Else
' ファイルが開けない場合(使用中など)の処理
Err.Clear
End If
End If
Next
On Error GoTo 0
' 3. CSV出力
WriteCSV dictResult, KEYWORDS, OUTPUT_CSV
' 4. 後片付け(オブジェクトの解放)
Set ts = Nothing
Set fso = Nothing
Set dictResult = Nothing
MsgBox "解析が完了しました!" & vbCrLf & "出力先: " & OUTPUT_CSV, vbInformation
End Sub
' --- サブルーチン: 文字列内の特定キーワード出現回数を数える ---
Function CountOccurrences(strText, strSearch)
Dim pos, occurrences
pos = 1
occurrences = 0
' 文字列内を検索し、見つかった位置を更新しながらループ
Do
pos = InStr(pos, strText, strSearch, vbTextCompare)
If pos > 0 Then
occurrences = occurrences + 1
pos = pos + Len(strSearch) ' 次の検索開始位置をずらす
Else
Exit Do
End If
Loop
CountOccurrences = occurrences
End Function
' --- サブルーチン: 集計結果をCSVとして書き出す ---
Sub WriteCSV(dict, keywords, outputPath)
Dim fso, ts, fileKey, wordKey, lineStr
Set fso = CreateObject("Scripting.FileSystemObject")
' 出力先フォルダがなければ作成
Dim outFolder
outFolder = fso.GetParentFolderName(outputPath)
If Not fso.FolderExists(outFolder) Then fso.CreateFolder(outFolder)
Set ts = fso.CreateTextFile(outputPath, True)
' ヘッダー行の作成 (ファイル名, Keyword1, Keyword2, ...)
lineStr = "FileName"
For Each wordKey In keywords
lineStr = lineStr & "," & wordKey
Next
ts.WriteLine lineStr
' データ行の作成
For Each fileKey In dict.Keys
lineStr = fileKey
For Each wordKey In keywords
lineStr = lineStr & "," & dict(fileKey)(wordKey)
Next
ts.WriteLine lineStr
Next
ts.Close
End Sub
3. 伝説のアーキテクトが教える「ここが重要!」
コードをただ眺めるだけでは、技術は血肉になりません。なぜこのような書き方をしたのか、その「意図」を理解してください。
① `Scripting.Dictionary` による二重構造の魔法
今回のコードの肝は、`dictResult` の持ち方です。
`dictResult(ファイル名)(キーワード) = 回数` という「辞書の辞書(Nested Dictionary)」構造を採用しています。
- なぜか? 普通の二次元配列だと、ファイル数やキーワード数が事前に決まっていないとサイズ変更が困難です。しかし、Dictionaryを使えば、ファイルが増えてもキーワードが増えても、動的に、かつメモリを最小限に抑えて格納できるのです。
② オブジェクトのライフサイクル管理
VBScriptは、スクリプト終了時に自動的にオブジェクトを解放してくれますが、大規模なループ処理を行う場合は注意が必要です。
`Set ts = file.OpenAsTextStream(1)` でファイルを開いたら、必ず `ts.Close` を呼び出してください。これを忘れると、「ファイルが別のプロセスで使用されているため、アクセスできません」というエラーに泣かされることになります。
③ 計算量(パフォーマンス)への意識
このスクリプトの計算量は $O(Files \times Lines \times Keywords)$ です。
もし、1つのファイルに数百万行ある場合、キーワードごとにファイルを開き直すと、処理時間は指数関数的に増大します。
今回のコードでは、「1つのファイルを1回だけ開き、その中で全キーワードをチェックする」という戦略をとっています。これが、実務で「速い」と言われるスクリプトの鉄則です。
4. 陥りやすい落とし穴(エラー回避術)
実務でこのスクリプトを走らせる際、以下の3点に注意してください。
1. 文字コードの壁:
`OpenAsTextStream` は標準ではシステム既定(多くの場合は ANSI/Shift-JIS)として動作します。もし解析対象のログが UTF-8 で書かれている場合、文字化けが発生します。その場合は、`ADODB.Stream` オブジェクトへの切り替えが必要になります。
2. 権限エラー:
`C:\Windows` や `C:\Program Files` などの保護されたフォルダをスキャンしようとすると、アクセス拒否エラーになります。必ず、ユーザー権限で読み書き可能なディレクトリを指定してください。
3. キーワードの重複:
`KEYWORDS` 配列に同じ単語を2つ入れてしまうと、集計結果が重複します。配列を定義する際は、一意であることを確認しましょう。
最後に:一歩先へ進むあなたへ
「マクロの記録」で書かれたコードは、いわば「決まった動きの再生」です。
しかし、今回のように「オブジェクトを組み合わせて、データ構造を設計する」コードは、いわば「思考の自動化」です。
このツールをベースに、「特定の時間帯だけ抽出したい」「エラーの内容をファイル名に含めたい」といった要望を自分で追加してみてください。その試行錯誤の先にこそ、エンジニアとしての真の力が宿ります。
ここをクリアすれば、VBScriptの基本はバッチリですよ。自信を持って、次の自動化に挑戦してください!
