【実務・中級編】【中級者向け】正規表現を用いた高度なメール振り分け:件名と本文のパターンマッチング – Outlook VBA解析バイブル

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

凡庸な「Like演算子」を捨てろ:正規表現とルールエンジンによる、極限のメール自動仕分け設計論

業務自動化の現場で、多くのエンジニアが陥る罠がある。それは、「件名に『請求書』が含まれていたらフォルダを移動する」といった、`Like`演算子や`InStr`関数による単純な文字列比較で、複雑な業務ロジックを構築しようとすることだ。

もし君が、`If mail.Subject Like “*[重要]*” Or mail.Subject Like “*注文*” Then…` といった、条件分岐の迷宮(Spaghetti Code)を書き始めているなら、今すぐその手を止めてほしい。それは「自動化」ではなく、単なる「コードによる負債の蓄積」だ。

実務におけるメールのパターンは、もっと残酷で、もっと複雑だ。
「注文番号 `ORD-12345` が件名に含まれている」
「本文中に `[承認依頼]` というタグがあり、かつその後に日付形式の文字列が続く」
「特定の正規化されたメールアドレス形式を持つ送信者からのメール」

これらを、いかにして「バグを許さず」「保守コストを最小限に抑え」「高パフォーマンスに」実装するか。本稿では、`VBScript.RegExp`を核とした、プロフェッショナルな「ルールエンジン型」の設計思想を伝授する。

1. なぜ「If文の羅列」は破綻するのか

設計者として、まず以下の3つのリスクを認識せよ。

1. 密結合(Tight Coupling)の恐怖: 振り分け条件(ビジネスロジック)と、メール監視の仕組み(インフラ層)が混ざると、条件を一つ変えるたびに監視ロジック全体を書き換え、再テストする必要が生じる。
2. パターンの脆弱性: `Like`演算子では、空白の有無、全角半角の揺らぎ、複雑な数字のパターンに対応できない。
3. パフォーマンスの劣化: 受信メールが急増した際、肥大化した`If-Else`ツリーを上から順に評価していく設計は、CPUサイクルを無駄に消費し、Outlookの応答性を著しく低下させる。

我々が目指すべきは、「条件(Pattern)」と「実行(Action)」を分離した、データ駆動型の設計である。

2. アーキテクチャ設計:ルールエンジンの概念

堅牢なシステムを作るには、以下の3層構造を意識せよ。

  • Rule Layer (データ層): 「どのパターン」を「どこへ」送るかという定義。
  • Engine Layer (ロジック層): 正規表現を用いてパターンを照合し、ルールを判定する心臓部。
  • Event Layer (インターフェース層): Outlookのイベントを検知し、Engineへ渡すトリガー。

3. 実装:プロダクション品質のコード例

ここでは、保守性を極限まで高めた実装例を示す。単なるコピペではなく、各オブジェクトの役割を理解してほしい。

A. クラスモジュール: `clsMailRule`

まず、ルールを「オブジェクト」として定義する。これにより、条件の追加・変更が容易になる。

' クラスモジュール名: clsMailRule
Option Explicit

Public Pattern As String         ' 正規表現パターン
Public TargetType As String      ' "Subject" または "Body"
Public ActionType As String      ' "Move", "Flag", "Read"
Public TargetValue As String     ' 移動先フォルダ名やフラグ設定値
Public IsActive As Boolean       ' ルールの有効/無効

' コンストラクタ的な役割の初期化メソッド
Public Sub Init(pPattern As String, pTarget As String, pAction As String, pValue As String)
    Me.Pattern = pPattern
    Me.TargetType = pTarget
    Me.ActionType = pAction
    Me.TargetValue = pValue
    Me.IsActive = True
End Sub

B. 標準モジュール: `modRegexEngine`

ここがシステムの心臓部だ。`VBScript.RegExp`を使い、ルールを評価する。

' 標準モジュール名: modRegexEngine
Option Explicit

' ルールセットを保持するコレクション
Private RuleCollection As Collection

' システムの初期化:ここでルールを定義する(本来はExcelやDBから読み込むのが理想)
Public Sub InitializeEngine()
    Set RuleCollection = New Collection
    Dim rule As clsMailRule
    
    ' ルール1: 件名に「ORD-数字5桁」が含まれる場合、特定のフォルダへ移動
    Set rule = New clsMailRule
    rule.Init "^ORD-\d{5}", "Subject", "Move", "注文管理フォルダ"
    RuleCollection.Add rule
    
    ' ルール2: 本文に「【重要】」が含まれ、かつ「期限:YYYY/MM/DD」の形式がある場合、フラグ付与
    Set rule = New clsMailRule
    rule.Init "【重要\].*期限:\d{4}/\d{2}/\d{2}", "Body", "Flag", "True"
    RuleCollection.Add rule
    
    ' ルール3: 特定のドメインからのメールを既読にする
    Set rule = New clsMailRule
    rule.Init "@critical-system\.com$", "Subject", "Read", ""
    RuleCollection.Add rule
End Sub

' メインロジック:メールを解析し、ルールに合致すればアクションを実行する
Public Sub ProcessMail(ByVal mail As Outlook.MailItem)
    Dim rule As clsMailRule
    Dim regEx As Object
    Set regEx = CreateObject("VBScript.RegExp")
    
    ' 正規表現の基本設定
    With regEx
        .Global = True
        .IgnoreCase = True
        .MultiLine = True
    End With
    
    On Error GoTo ErrorHandler
    
    For Each rule In RuleCollection
        If Not rule.IsActive Then GoTo NextRule
        
        ' 判定対象のテキストを決定
        Dim targetText As String
        If rule.TargetType = "Subject" Then
            targetText = mail.Subject
        Else
            targetText = mail.Body ' 本文が巨大な場合は、必要に応じてHTMLBodyとの使い分けを検討せよ
        End If
        
        ' 正規表現マッチング実行
        regEx.Pattern = rule.Pattern
        If regEx.Test(targetText) Then
            ' マッチした場合、アクションを実行
            ExecuteAction mail, rule
            Exit Sub ' 一つのメールに対して一つのアクションで終了する場合
        End If

NextRule:
    Next rule

    Exit Sub

ErrorHandler:
    Debug.Print "Error processing mail: " & Err.Description
    ' 実務ではここでログファイルへの書き出しを行うこと
End Sub

' アクション実行部:ここで具体的な処理を分離する
Private Sub ExecuteAction(ByVal mail As Outlook.MailItem, ByVal rule As clsMailRule)
    Select Case rule.ActionType
        Case "Move"
            ' フォルダ移動ロジック(フォルダの存在チェックを忘れるな)
            Dim targetFolder As Outlook.MAPIFolder
            Set targetFolder = mail.Parent.Session.GetDefaultFolder(olFolderInbox).Folders(rule.TargetValue)
            mail.Move targetFolder
            
        Case "Flag"
            mail.FlagRequest.MarkAsTask
            mail.Importance = olImportanceHigh
            
        Case "Read"
            mail.UnRead = False
    End Select
End Sub

C. ThisOutlookSession

イベントハンドラは、極限までシンプルに保つ。

' ThisOutlookSession
Option Explicit

Private WithEvents objItems As Outlook.Items

Private Sub Application_Startup()
    ' 受信トレイの監視を開始
    Dim inbox As Outlook.Folder
    Set inbox = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
    Set objItems = inbox.Items
    
    ' エンジンの初期化
    Call InitializeEngine
    Debug.Print "Engine Started."
End Sub

' NewMailExは、大量のメールが同時に届いた際にも、
' 各メールのEntryIDを確実に捕捉できるため、ItemAddより推奨される
Private Sub Application_NewMailEx(ByVal EntryID As String)
    Dim ns As Outlook.NameSpace
    Dim item As Object
    
    Set ns = Application.GetNamespace("MAPI")
    Set item = ns.GetItemFromID(EntryID)
    
    ' MailItemであることを確認してからエンジンへ渡す
    If TypeOf item Is Outlook.MailItem Then
        Call ProcessMail(item)
    End If
End Sub

4. プロフェッショナルが守るべき「極限の注意点」

① `mail.Body` vs `mail.HTMLBody` の戦い

`mail.Body` はプレーンテキストを返すが、`mail.HTMLBody` はHTMLソースを返す。
HTMLBodyは解析の負荷が非常に高く、正規表現のパターンもタグ(`

`等)の影響を受ける。「テキストパターンのみ」を対象にするなら、迷わず `Body` を選択せよ。 パフォーマンスの差は歴然だ。

② オブジェクトのライフサイクル

`VBScript.RegExp` オブジェクトを、ループの中で毎回 `CreateObject` してはならない。本稿の例のように、メソッド内で一度生成して使い回すか、あるいはクラスのメンバ変数として保持し、再利用することで、メモリ確保のオーバーヘッドを最小化できる。

③ 堅牢なエラーハンドリング

自動化ツールがエラーで止まると、ユーザーの業務が止まる。
`On Error Resume Next` は「思考停止」の証だ。必ず `ErrorHandler` を用意し、「どのメールが、どのルールで、なぜ失敗したのか」をログに残す設計にしろ。

④ 外部連携(Excel/DB)への拡張

実務では、ルールをVBAコード内にハードコードするのは素人の仕事だ。
`InitializeEngine` 内で、Excelシートや外部のJSONファイル、あるいはSQL Serverからルールをロードするように変更せよ。そうすれば、「VBAコードを触らずに、業務ルールだけを更新する」という、真の運用保守が可能になる。

結論

正規表現を用いたメール振り分けは、単なる「テクニック」ではない。それは、「変化し続けるビジネスルールを、いかにして静的なシステムに組み込むか」という設計思想の戦いである。

`Like`演算子による場当たり的な実装を卒業し、オブジェクト指向に基づいた「ルールエンジン」を構築せよ。その先にのみ、真に堅牢で、メンテナンスフリーな自動化の世界が待っている。

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