【入門編】【実務中級者向け】複数テキストファイル内の特定検索キーワード出現頻度をFSOでカウントしクロス集計CSVを出力するツール – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

【極限の知見】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の基本はバッチリですよ。自信を持って、次の自動化に挑戦してください!

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