【実務・中級編】【実務中級者向け】拡張子(.pdf / .xlsx / .csv)に応じてファイルを対応する自動生成サブフォルダへ全自動振り分けする整理ツール – VBScript (Visual Basic Scripting Edition)解析バイブル

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

自動化の混沌を支配せよ:VBScriptによる「堅牢な」ファイル整理アーキテクチャの設計思想

「ファイルがバラバラで作業が進まないから、とりあえず拡張子ごとに分けるスクリプトを書いた」

もし君が、単に `If…ElseIf` を並べてファイルを移動させるだけのコードを書こうとしているなら、今すぐその手を止めてほしい。それは「自動化」ではない。ただの「不安定なコードの量産」だ。

実務において、ファイル操作スクリプトが直面するのは、美しく整理された理想の世界ではない。「使用中のファイルによるロック」「同名ファイルの衝突」「権限不足による書き込みエラー」「予期せぬネットワーク切断」といった、泥臭い例外の嵐だ。

本稿では、VBScriptの `FileSystemObject (FSO)` を使い倒し、中級者が一段上の「エンジニア」へと昇華するための、例外に強く、保守性の高いファイル整理ツールの設計と実装を伝授する。

1. なぜ、君のスクリプトは「現場」で使い物にならないのか

多くの初心者が陥る罠は、「正常系」しか考えていないことだ。

非効率な設計の典型例

  • ハードコーディングの乱用: 拡張子とフォルダ名の対応をコード内にベタ書きしている。新しい拡張子(例: `.docx`)が増えるたびに、ロジックそのものを書き換える必要がある。
  • エラーハンドリングの欠如: 移動先に同名のファイルがあった瞬間、スクリプトは悲鳴を上げて停止する。
  • リソース管理の甘さ: `FileSystemObject` をループのたびに生成したり、適切に解放しなかったりする。これは大規模な処理において、メモリリークやファイルハンドルの枯渇を招く。

プロフェッショナルが目指すべき設計指針

1. データとロジックの分離: 「どの拡張子をどこへ」という定義(マッピング)を、ロジックから独立させる。
2. 冪等性(べきとうせい)の確保: 何度実行しても、エラーにならず、同じ正しい状態に収束すること。
3. 防御的プログラミング: 「フォルダはあるか?」「ファイルはロックされていないか?」を、操作の直前に必ず検証する。

2. 実装の核心:拡張子マッピング・アーキテクチャ

今回の設計では、`Scripting.Dictionary` オブジェクトを活用する。これにより、拡張子と移動先フォルダの紐付けを「定義データ」として管理し、ロジックの変更なしに拡張性を確保する。

実装すべきロジックのフロー

1. 初期化: FSOおよびDictionaryのインスタンス化。
2. 定義: 拡張子とターゲットフォルダのペアをDictionaryに格納。
3. 走査: 対象ディレクトリ内のファイルを一つずつチェック。
4. 判定: 拡張子がDictionaryに含まれているか判定。
5. 実行:

  • ターゲットフォルダの存在確認(なければ作成)。
  • 移動先への同名ファイル存在確認(あればスキップ、またはログ出力)。
  • ファイル移動の実行。

6. 後処理: オブジェクトの解放。

3. プロダクション・グレードのコード例

以下に、そのまま実務投入可能なレベルのスクリプトを示す。コメントを読み、なぜこの書き方をしているのかを深く理解してほしい。

' ==============================================================================
' Script Name: Professional File Organizer
' Description: 拡張子に基づきファイルを自動分類する堅牢な整理ツール
' Author: Chief Architect
' ==============================================================================
Option Explicit

' --- 設定領域 (Configuration) ---
Const TARGET_DIR = "C:\Users\YourName\Downloads\WorkArea" ' 対象ディレクトリ
Const LOG_FILE   = "C:\Users\YourName\Downloads\WorkArea\organize_log.txt"

' --- メイン処理 ---
Main

Sub Main()
    Dim fso, dict, targetFolder, file, ext, destFolder
    
    ' オブジェクトの生成
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set dict = CreateObject("Scripting.Dictionary")

    ' 1. 拡張子マッピングの定義 (ロジックと分離されたデータ層)
    ' キー: 拡張子 (ドットなし), 値: 移動先フォルダ名
    dict.Add "pdf",  "Documents_PDF"
    dict.Add "xlsx", "Spreadsheets_Excel"
    dict.Add "xls",  "Spreadsheets_Excel"
    dict.Add "csv",  "Data_CSV"
    dict.Add "jpg",  "Images"
    dict.Add "png",  "Images"

    ' 対象ディレクトリの存在確認
    If Not fso.FolderExists(TARGET_DIR) Then
        MsgBox "エラー: 対象ディレクトリが見つかりません: " & TARGET_DIR, vbCritical
        Exit Sub
    End If

    Set targetFolder = fso.GetFolder(TARGET_DIR)
    
    WriteLog "--- 整理プロセス開始 ---"

    ' 2. ファイル走査ループ
    For Each file In targetFolder.Files
        ' ログファイル自体を処理対象から外す(無限ループ防止)
        If LCase(file.Name) <> LCase(fso.GetFileName(LOG_FILE)) Then
            
            ext = LCase(fso.GetExtensionName(file.Path))
            
            ' 3. 辞書に登録されている拡張子か判定
            If dict.Exists(ext) Then
                destFolder = TARGET_DIR & "\" & dict(ext)
                
                ' 4. 安全な移動プロセスを実行
                MoveFileSafely fso, file, destFolder
            End If
        End If
    Next

    WriteLog "--- 整理プロセス終了 ---"
    
    ' オブジェクトの解放 (リソース管理の徹底)
    Set dict = Nothing
    Set fso = Nothing
    
    MsgBox "整理が完了しました。詳細はログを確認してください。", vbInformation
End Sub

' --- サブルーチン: 安全なファイル移動 ---
' 引数: fso, 対象ファイルオブジェクト, 移動先フォルダパス
Sub MoveFileSafely(fso, fileObj, destPath)
    On Error Resume Next ' エラー発生時にスクリプトを止めず、後続の処理へ
    
    ' ターゲットフォルダの動的作成
    If Not fso.FolderExists(destPath) Then
        fso.CreateFolder(destPath)
        If Err.Number <> 0 Then
            WriteLog "ERROR: フォルダ作成失敗 [" & destPath & "] - " & Err.Description
            Err.Clear
            Exit Sub
        End If
    End If

    Dim destinationFilePath
    destinationFilePath = destPath & "\" & fileObj.Name

    ' 同名ファイルの衝突回避
    If fso.FileExists(destinationFilePath) Then
        WriteLog "SKIP: 同名ファイルが既に存在します [" & fileObj.Name & "]"
        Err.Clear
        Exit Sub
    End If

    ' ファイル移動の実行
    fileObj.Move destPath & "\"
    
    If Err.Number <> 0 Then
        ' ファイルがロックされている場合などのエラーハンドリング
        WriteLog "ERROR: 移動失敗 [" & fileObj.Name & "] - " & Err.Description
        Err.Clear
    Else
        WriteLog "SUCCESS: 移動完了 [" & fileObj.Name & " -> " & destPath & "]"
    End If
    On Error GoTo 0 ' エラーハンドリングを元に戻す
End Sub

' --- サブルーチン: 簡易ログ出力 ---
Sub WriteLog(message)
    Dim fso, logFile
    Set fso = CreateObject("Scripting.FileSystemObject")
    ' 追記モード (8) で開く
    Set logFile = fso.OpenTextFile(LOG_FILE, 8, True)
    logFile.WriteLine Now & " : " & message
    logFile.Close
    Set logFile = Nothing
End Sub

4. アーキテクトの視点:このコードが「強い」理由

① `On Error Resume Next` の局所的使用

初心者はスクリプトの冒頭に `On Error Resume Next` を書き、すべてのエラーを無視しようとする。これは自殺行為だ。
私の設計では、「失敗する可能性がある最小限の操作(移動やフォルダ作成)」の直前・直後のみに適用している。これにより、予期せぬ致命的なエラーは検知しつつ、ファイルロックによる一時的なエラーでプロセス全体が止まるのを防いでいる。

② 拡張子マッピングの柔軟性

`dict.Add` の行を増やすだけで、新しいファイル形式に対応できる。ロジック(`Main`内のループ)を一切触る必要がない。これは「Open-Closed Principle(開放閉鎖の原則)」に基づいた、保守性の高い設計だ。

③ ログによる「事後検証」の実現

自動化ツールにおいて、最も重要なのは「何が起きなかったか」を知ることだ。
「移動に失敗したファイルはどれか?」「なぜスキップされたのか?」をログに残すことで、ユーザーはスクリプトを盲信せず、管理可能な状態で運用できる。

5. さらなる高みへ:実務での応用

このツールをベースに、さらに以下の機能を実装することで、君のツールは「神ツール」へと進化する。

  • データベース連携: `Scripting.Dictionary` の代わりに、ExcelシートやSQLiteからマッピング情報を読み込む。これにより、非エンジニアでも設定変更が可能になる。
  • タイムスタンプによるサブフォルダ化: `destPath & “\” & Year(file.Date) & “年”` のように、日付による階層化を行う。
  • タスクスケジューラとの統合: Windowsのタスクスケジューラに登録し、1時間に1回実行させる。これで「整理」という概念そのものが消滅する。

結論

VBScriptは古い言語かもしれない。しかし、その挙動を完全に掌握し、例外を制御下に置いたとき、それは強力な武器となる。
「動けばいい」という思考を捨て、「どう壊れるか」を予測して書くこと。それが、プロフェッショナルなエンジニアへの第一歩だ。

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