【ExcelVBA】ファイルパスからファイル名を取り出す2つの方法の比較

# はじめに

ExcelVBAで業務用のちょっとしたツールを作る時に、ファイルパスからファイル名を取り出す処理を作ることがあります。
ファイル名を取り出す方法として、組み込みの`Dir`関数を使う方法と、`FileSystemObject.GetFileName`を使う方法の2つが知られていますが、両者の機能的な違いをよく理解せずに使っていたので、少し調べてみた結果を記事としてまとめてみました。

# Dir関数を使う方法

[Dir関数のリファレンス](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/dir-function)を読むと、「Dirは、pathname に一致する最初のファイル名を返します。」と書かれています。
加えて、「一致するファイル名がなくなると、Dir は長さ 0 の文字列 ("") を返す」という仕様も書かれていました。

つまり、`Dir`関数の引数としてファイルパスを指定した時、以下のような挙動になることが分かります。

1. 引数で指定したファイルパスが存在する時、ファイル名が返される。
2. 引数で指定したファイルパスが存在しない時、空文字が返される。

```vb:Dir関数を使ったサンプルコード
Option Explicit

' 存在するファイルパス
Private Const FILE_PATH As String = "C:\Users\NKOJIMA\Documents\sample.txt"
' 存在しないファイルパス
Private Const INVALID_FILE_PATH As String = "C:\Users\NKOJIMA\Documents\sample2.txt"

' Dir関数を使って、ファイルパスからファイル名を抽出する。
Sub GetFileNameByDir()
    Debug.Print Dir(FILE_PATH)          ' 存在するファイルの場合、ファイル名が返される
    Debug.Print Dir(INVALID_FILE_PATH)  ' 存在しないファイルの場合、空文字が返される
End Sub
```

```text:Dir関数を使ったサンプルコードの実行結果
sample.txt
                ←空文字が出力されている
```

# FileSystemObjectを使う方法

[FileSystemObject.GetFileNameのリファレンス](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/getfilename-method-visual-basic-for-applications)を読むと、「指定したパスの最後のコンポーネントを、ドライブの指定の一部ではないものとして返します」と書かれています。
加えて、以下の注意書きもありました。

* （引数の）pathspecが名前付きのコンポーネントで終了していない場合、GetFileNameメソッドは長さがゼロの文字列 ("") を返します。
* GetFileNameメソッドは指定されたパス文字列のみを処理します。 パスの解決は試行されず、指定されたパスの存在も確認しません。

つまり、FileSystemObject.GetFileNameの引数としてファイルパスを指定した時、以下のような挙動になることが分かります。

1. 引数で指定したファイルパスの存在をチェックせず、機械的にファイルパスの最後のコンポーネント（≒ファイル名）が返される。

また、以下のテストコードで確認したところ、引数で指定したファイルパスがドライブレターの時は空文字が返されました。
リファレンスが分かりにくい日本語ですが、どうやらドライブレターを引数にすると「引数が名前付きのコンポーネントで終了していない場合」に該当するようです。

```vb:FileSystemObjectを使ったサンプルコード
Option Explicit

' 存在するファイルパス
Private Const FILE_PATH As String = "C:\Users\NKOJIMA\Documents\sample.txt"
' 存在しないファイルパス
Private Const INVALID_FILE_PATH As String = "C:\Users\NKOJIMA\Documents\sample2.txt"

' FileSystemObjectを使って、ファイルパスからファイル名を抽出する。
Sub GetFileNameByFSO()
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' 存在するファイルの場合、ファイル名が返される
    Debug.Print fso.GetFileName(FILE_PATH)
    ' 存在しないファイルの場合でも、ファイル名が返される。
    Debug.Print fso.GetFileName(INVALID_FILE_PATH)
    ' ドライブレターを指定すると、空文字が返される。
    Debug.Print fso.GetFileName("C:\")
End Sub
```

```text:FileSystemObjectを使ったサンプルコードの実行結果
sample.txt
sample2.txt
                ←空文字が出力されている
```

# どちらが実用的か？

`Dir`関数は実質的に「引数で指定したファイルパスの存在をチェックしてくれる」という機能も内包しているため、様々なシーンで使いやすいと思います。
[こちらのページ](http://officetanaka.net/excel/vba/tips/tips78.htm)を見ると、ファイルパスからファイル名を取り出すシンプルな方法として`Dir`関数が紹介されています。

一方で、`FileSystemObject`は引数で指定したファイルパスの存在をチェックしないため、存在しない可能性のあるファイルパスや架空のファイルパスに対する処理には向いていると思います。
またVBAに不慣れな人がコードを保守する場合、「`Dir`関数の戻り値がファイル名」というのはパッと想像しづらいと思うので、`FileSystemObject`を使った方がコードの可読性が向上するのではと感じました。

両者とも甲乙つけ難い...というより、帯に短し襷に長しという感じなので、状況に応じて使い分けるのが良いのかもしれません。

# 参考URL

* [Dir関数](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/dir-function)
* [FileSystemObject.GetFileName](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/getfilename-method-visual-basic-for-applications)
* [フルパスをパスとファイル名に分ける](http://officetanaka.net/excel/vba/tips/tips78.htm)