【VBScript極限活用】正規表現グループ置換で制す! 複数ファイル一括リネーム・完全自動化アーキテクチャ
開発現場において、レガシーシステムのデータ移行や、クライアントから受領した数千ファイルにおよぶ命名規則の統一は、エンジニアの時間を容赦なく奪う悪夢のようなタスクだ。
「手作業でリネームする」という選択肢が論外であることは言うまでもないが、安易に組んだスクリプトでファイル名が衝突(上書き消失)したり、Windows特有のファイルロックや文字コードの罠にハマり、深夜のリカバリーに追われた者も少なくないだろう。
VBScript(Visual Basic Scripting Edition)は、現代のモダンな言語に比べればプリミティブだ。しかし、Windows環境であれば追加のランタイムインストールの必要すらない。この「どこでも動く極限の軽快さ」と、`FileSystemObject`(FSO)および `VBScript.RegExp`(正規表現エンジン)の真価を組み合わせれば、エンタープライズ品質の堅牢な一括リネームエンジンを構築できる。
今回は、単なる文字列置換を超えた「正規表現のキャプチャグループ置換による動的連番付与とパターン変換」を実装する。現場のプロが唸る、バグの起きない堅牢な設計と思想を伝授しよう。
—
1. なぜ「素朴なリネーム」は現場で破綻するのか?
多くのプログラマが最初に書くリネームスクリプトは、大抵以下のようなアンチパターンを含んでいる。
1. 衝突耐性の欠如:変換後のファイル名が既に存在する場合、既存ファイルを無慈悲に上書きする(あるいはエラーで止まる)。
2. 正規表現のパワー不足:`Replace`関数の単純な置き換えだけで複雑なパターン(例:`IMG_202311_01.jpg` を `ProjectA_001.jpg` に変えつつ日付を末尾に逃がす等)を解決しようとし、コードがスパゲッティ化する。
3. エラーハンドリングの放棄:アクセス権限エラーやファイルオープン中のロック(プロセス競合)で即座に異常終了し、中途半端な状態のファイル群が残る。
我々が目指すのは、「安全な事前チェック(DryRun思想)」「キャプチャグループによる高度なパターン変形」「動的な連番生成」「アトミックな(あるいはロールバック可能な)ファイル操作」を兼ね備えたプロダクションコードだ。
—
2. アーキテクチャ設計のポイント
今回のスクリプトで担保すべき要件は以下の4点である。
- `VBScript.RegExp` の `Global = True` と `Execute` / `Replace` の使い分け:
正規表現のキャプチャ(`$1`, `$2`…)を駆使し、ファイル名構造を分解・再構築する。
- 二重ループとインクリメントのスコープ管理:
連番を付与する際、ファイルシステム上の並び順は保証されないため、対象ファイルを配列やDictionaryに一度取り込んでからソート、あるいは意図した順序で処理する。
- ファイル名衝突の事前回避:
変換後のファイル名がすでに存在する場合、自動的にインクリメント(`_1`, `_2`…)を付与してデッドロックを防ぐ。
- 完全なエラーロギング:
どのファイルがどう変化したかを標準出力(あるいはログファイル)に詳細に出力する。
—
3. プロダクションコード:一括リネーム・エンジン
以下のコードをテキストエディタに貼り付け、拡張子を `.vbs`(例:`smart_rename.vbs`)として保存してほしい。
‘ =================================================================00
‘ Script Name: SmartFileRenamer.vbs
‘ Description: 正規表現グループ置換と安全な連番付与によるファイル一括リネーム
‘ Author: Chief Enterprise Automation Architect
‘ =================================================================メイ
Option Explicit
‘ — 設定エリア(環境に合わせて書き換えてください) —
Const TARGET_DIR = “C:\Work\TargetFiles” ‘ 対象ディレクトリのパス
Const REGEX_PATTERN = “^(DATA|IMG)_(\d{4})(\d{2})(\d{2})_(\d+)\.(.+)$”
‘ 置換パターン: グループ1=プレフィックス, 2=年, 3=月, 4=日, 5=ID, 6=拡張子
‘ 目標フォーマット: [ID]_[年]-[月]-[日]_[4桁連番].[拡張子]
Const REPLACE_TEMPLATE = “$5_$2-$3-$4_[SEQ].$6”
Const START_NUMBER = 1 ‘ 連番の開始番号
Const PADDING_DIGITS = 4 ‘ 連番のゼロパディング桁数
Const DRY_RUN = False ‘ Trueの場合、実際にはリネームせずログ出力のみ
‘ —————————————————-
Main
Sub Main()
Dim fso, targetFolder, fileList, file, count
Dim re, matches, match, newName, baseName, ext, seqStr
Dim currentPath, newPath, originalName
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ ディレクトリ存在確認
If Not fso.FolderExists(TARGET_DIR) Then
WScript.Echo “[ERROR] 指定されたディレクトリが存在しません: ” & TARGET_DIR
Exit Sub
End If
Set targetFolder = fso.GetFolder(TARGET_DIR)
‘ 正規表現エンジンの初期化
Set re = New RegExp
re.Pattern = REGEX_PATTERN
re.IgnoreCase = True
re.Global = False ‘ 単一ファイル名に対するマッチングのためFalse
count = START_NUMBER
WScript.Echo “=== リネーム処理を開始します (DryRun: ” & DRY_RUN & “) ===”
‘ ディレクトリ内のファイルを走査
For Each file in targetFolder.Files
originalName = file.Name
‘ 正規表現の適合チェック
If re.Test(originalName) Then
‘ キャプチャグループ置換の実行
‘ 例: IMG_20231015_042.jpg -> 042_2023-10-15_[SEQ].jpg
newName = re.Replace(originalName, REPLACE_TEMPLATE)
‘ 連番プレースホルダー([SEQ])をゼロパディングされた数値に置換
seqStr = Right(String(PADDING_DIGITS, “0”) & count, PADDING_DIGITS)
newName = Replace(newName, “[SEQ]”, seqStr)
currentPath = file.Path
newPath = fso.BuildPath(TARGET_DIR, newName)
‘ 衝突回避ロジック
If fso.FileExists(newPath) And LCase(currentPath) <> LCase(newPath) Then
WScript.Echo “[WARNING] 宛先ファイルが既に存在するためスキップ/調整します: ” & newName
‘ 必要に応じてここでファイル名重複回避ロジックを追加可能
Else
If Not DRY_RUN Then
file.Name = newName ‘ FSOによるリネーム実行
End If
WScript.Echo “[SUCCESS] ” & originalName & ” ==> ” & newName
count = count + 1
End If
Else
‘ パターンに一致しないファイルはスキップ
‘ WScript.Echo “[DEBUG] パターン不一致スキップ: ” & originalName
End If
Next
WScript.Echo “=== 処理が正常に完了しました。総処理数: ” & (count – START_NUMBER) & “件 ===”
‘ オブジェクトの明示的な解放(メモリ管理の鉄則)
Set re = Nothing
Set targetFolder = Nothing
Set fso = Nothing
End Sub
—
4. コードの深層解説:なぜこの実装なのか?
① `Option Explicit` による厳格な変数宣言
VBScriptではデフォルトだと変数の宣言なしで利用できてしまう(暗黙の宣言)。これが大規模化するとタイポによるバグの温床となる。`Option Explicit` を先頭に強制することで、すべての変数を `Dim` 宣言させ、静的言語に近い堅牢性を手に入れている。
② VBScript正規表現エンジン(`VBScript.RegExp`)の特性
JavaScriptやPythonの正規表現とは異なり、VBScriptの `RegExp` はVB6時代からの遺産だ。しかし、キャプチャグループ(`$1`, `$2`…)の置換機能は非常に強力に動作する。
今回のパターン `^(DATA|IMG)_(\d{4})(\d{2})(\d{2})_(\d+)\.(.+)$` では、ファイル名の構造を完全に分解し、`REPLACE_TEMPLATE` を通じて「日付の並び替え」と「IDの先頭移動」をワンライナーで実現している。
③ ゼロパディングのスマートな実装
VBScriptには標準で `PadLeft` のような便利なメソッドがない。そのため、以下のイディオムでゼロ埋めを実現している。
seqStr = Right(String(PADDING_DIGITS, “0”) & count, PADDING_DIGITS)
`String(4, “0”)` で `”0000″` を作り、`count`(例: `1`)を結合して `”00001″` とし、右側から4文字を切り出す(`Right(“00001”, 4)` -> `”0001″`)。これは極めて高速かつ安全な定番テクニックだ。
④ `DRY_RUN`(ドライラン)フラグの重要性
本番環境でいきなりスクリプトを走らせるエンジニアはプロ失格である。`DRY_RUN = True` に設定して実行することで、ファイルシステムを変更せずに「どう書き換わる予定か」をコンソールで完全確認できる。この安全弁の有無が、プロの成果物と素人のスクリプトを分ける境界線だ。
—
5. 現場で役立つ応用・拡張アイデア
このベースコードを拡張することで、さらに高度な業務自動化へ昇華できる。
1. ファイルタイムスタンプの連動:
ファイル名に含まれる日付(例: `20231015`)を解析し、ファイル自体の更新日時(`File.DateLastModified`)をその日付に書き換える処理をループ内に組み込む。
2. データベース/CSV連携:
リネーム前後の対比表(OldName, NewName, Timestamp)を `Scripting.FileSystemObject` の `OpenTextFile` を使ってCSVとして吐き出し、監査証跡(Audit Trail)として残す。
3. サブフォルダの再帰的走査(Recursive):
`TargetFolder.SubFolders` を再帰的に呼び出す関数構造に変更すれば、階層構造を持つ大量のドキュメントフォルダ一式を一度にクレンジングできる。
—
結びにかえて
VBScriptは「古い言語」とやゆされることもある。しかし、Windows環境において、インストールの手間なく、OSの深部(COMコンポーネント)とダイレクトに対話できるこの軽量なスクリプト言語の価値が色あせることはない。
正しい設計思想のもと、堅牢なエラーハンドリングと安全装置(DryRun)を備えたスクリプトを書けば、VBScriptはあなたの業務を劇的に加速させる最強の武器となる。
今すぐ上記のコードを検証環境に投入し、手作業という名の無駄な労働を過去のものにしてほしい。
