【実務・中級編】【フォルダ走査】GetFolderとFilesコレクションを用いた指定拡張子(.csv / .xlsx)ファイルの一覧取得 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【VBScript極限講座】FSOを用いた堅牢なフォルダ走査:指定拡張子(.csv / .xlsx)抽出のアンチパターンと正解

開発現場において、指定フォルダ内のファイルを走査し、特定の拡張子(`.csv` や `.xlsx`)を持つものだけをピックアップして処理する要件は、RPAやバッチ処理の基本中の基本だ。

しかし、この「基本」こそが、素人とプロのアーキテクトの腕の差が最も如実に現れる領域でもある。ネット上の適当なサンプルコードをコピペして現場に投入した結果、「ファイル名に日本語や特殊文字が含まれていると文字化けする」「共有フォルダのネットワーク遅延で突然死する」「ロックファイルを誤検知してバッチが異常終了する」といった障害を引き起こすケースを、私は幾度となく目撃してきた。

今回は、VBScriptのFile System Object(FSO)と `Files` コレクションを極限までチューニングし、実務の荒波に耐えうる「バグの起きない堅牢なフォルダ走査設計」を伝授する。

—

1. なぜ「よくあるコード」は実務で破綻するのか?(アンチパターン)

まずは、よく見かける典型的な「危ういコード」を見てみよう。

‘ 【NG例】どこに地雷が埋まっているかわかるだろうか?
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set folder = fso.GetFolder(“C:\Data”)

For Each file in folder.Files
‘ 拡張子の判定が甘い
If UCase(Right(file.Name, 4)) = “.CSV” Then
‘ 処理を実行
ProcessFile file.Path
End If
Next

このコードの何が問題か。プロの視点から3つの致命傷を指摘する。

1. 拡張子判定の脆弱性 (`Right(file.Name, 4)`):
ファイル名が `test.tar.gz` だった場合や、拡張子が3文字でないケース(`.xlsx` は4文字)に対応できない。マジックナンバー(文字数の固定値)に頼る実装は必ず破綻する。
2. 文字コード(CodePage)とロケールの罠:
VBScriptのランタイムはOSのANSIコードページに依存する。特にマルチバイト文字(日本語)を含むパスを扱う際、明示的なエラーハンドリングやオブジェクトのライフサイクル管理を行わないと、ファイルを見失う。
3. 例外(ロックファイル・アクセス権)への無防備:
他のプロセス(Excelなど)が開いているファイルや、アクセス権がないファイルを掴んだ瞬間、容赦なく「権限がありません (Permission denied)」エラーでスクリプトがクラッシュする。

—

2. プロダクション品質を満たす設計思想

堅牢な業務自動化ツールを構築するためには、以下の要件を満たさなければならない。

  • FSOの適切なオブジェクト解放: メモリリークを防ぐため、参照したオブジェクトはスコープごとに明確に解放する。
  • 正確な拡張子の抽出: `fso.GetExtensionName()` メソッドを使用し、文字数に依存しない堅牢な判定を行う。
  • 防御的プログラミング: 想定外のファイルや一時ファイル(`~$.xlsx` など)を自動的にスキップするフィルター機構を設ける。

—

3. 【コピペ即実戦投入可】堅牢なフォルダ走査・拡張子抽出コード

それでは、実務の現場でそのまま使えるプロダクションコードを提示する。エラーハンドリング、拡張子の正規化比較、そして一時ファイルの除外ロジックを完璧に組み込んだ決定版だ。

Option Explicit

‘ ==============================================================================
‘ 処理メイン実行部
‘ ==============================================================================
Sub Main()
Dim targetDir
targetDir = “C:\BusinessData\TargetFolder” ‘ ※実環境に合わせて変更してください

Call ProcessSpecifiedFiles(targetDir)
End Sub

‘ ==============================================================================
‘ 指定フォルダ内の .csv および .xlsx ファイルを安全に走査・処理する関数
‘ ==============================================================================
Sub ProcessSpecifiedFiles(ByVal folderPath)
Dim fso, targetFolder, fileList, targetFile
Dim ext, fileName
Dim processedCount

processedCount = 0

‘ FSOのインスタンス生成
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ フォルダの存在チェック(防御的プログラミングの基本)
If Not fso.FolderExists(folderPath) Then
WScript.Echo “【エラー】指定されたフォルダが存在しません: ” & folderPath
Exit Sub
End If

Set targetFolder = fso.GetFolder(folderPath)
Set fileList = targetFolder.Files

WScript.Echo “=== フォルダ走査開始: ” & folderPath & ” ===”

‘ ファイルコレクションのループ処理
For Each targetFile In fileList
fileName = targetFile.Name

‘ 1. Excelの編集時一時ファイル(~$で始まるファイル)は明示的にスキップ
If Left(fileName, 2) = “~$” Then
‘ スキップ
Else
‘ 2. 拡張子の安全な取得と小文字化(大文字小文字の揺れを完全に吸収)
ext = LCase(fso.GetExtensionName(targetFile.Path))

‘ 3. 対象拡張子(csv または xlsx)の判定
Select Case ext
Case “csv”, “xlsx”
WScript.Echo “処理対象検出: ” & fileName & ” (拡張子: .” & ext & “)”

‘ ————————————————–
‘ ここに実際のファイル処理ルーチンを記述
‘ 例: Call ExecuteYourBusinessLogic(targetFile.Path)
‘ ————————————————–

processedCount = processedCount + 1

Case Else
‘ 対象外の拡張子はスルー
End Select
End If
Next

WScript.Echo “=== 走査完了. 処理対象ファイル数: ” & processedCount & ” 件 ===”

‘ オブジェクトの明示的な解放(メモリ管理の鉄則)
Set fileList = Nothing
Set targetFolder = Nothing
Set fso = Nothing
End Sub

‘ メイン処理のキック
Main()

—

4. コードの解説とアーキテクトからのワンポイントアドバイス

① `fso.GetExtensionName()` の優位性

前述の通り、`Right` 関数を使った文字列切り出しは脆弱だ。`GetExtensionName` メソッドは、ファイルパスから確実に「最後のドット以降の文字列」を抽出してくれるため、`file.name` にドットが複数含まれる複雑なファイル名(例:`report.2023.10.xlsx`)であっても正確に `.xlsx` を認識できる。

② Excelの一時ファイル(`~$`)対策

業務で `xlsx` を扱う際、ファイルが開かれていると自動生成される隠し一時ファイル(`~$filename.xlsx`)がフォルダ内に常駐することがある。これを誤って処理対象に含めると「ファイルが別のプロセスによって使用されています」エラーを誘発する。冒頭の `Left(fileName, 2) = “~$”` による弾き処理は、実務では必須のノウハウだ。

③ オブジェクトライフサイクルの管理

VBScriptのガベージコレクションは頼りにならない。特にCOMオブジェクトであるFSOやコレクションを扱う際は、処理の最後に `Set targetFile = Nothing` や `Set fso = Nothing` と明示的に解放する癖をつけておこう。これが長期間稼働するRPAや常駐バッチのメモリリークを防ぐ唯一の盾となる。

—

総括

ファイルの走査と拡張子判定という一見地味な処理であっても、プロのエンジニアが書いたコードには「予期せぬ例外を防ぐための防壁」が何重にも張り巡らされている。

今回紹介したパターンをベースに実装を行えば、現場でありがちな「謎のエラーによるバッチ停止」を撲滅することができるはずだ。手元の環境に合わせてパスを書き換え、その堅牢性を体感してほしい。

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