アーカイブ

【.NET】System.IO.Directory.GetFiles / EnumerateFiles のワイルドカード指定は期待通りの動作にならない場合があるという話

.NETのファイル操作にまつわる小ネタ。

目次
  1. 現象
    1. サンプル
  2. 原因
  3. 解決策
    1. サンプルコード(C#)
    2. 実行結果
  4. おわりに

現象

.NETでファイル一覧を取得する際、特定の拡張子のみを取得したい場合があります。

そのような場合は、ファイル一覧を取得する以下の処理

  • System.IO.Directory.GetFiles
  • System.IO.Directory.EnumerateFiles

を使用しますよね。上記2つの処理はワイルドカードを指定して対象のファイルを絞ることができます。

特定の拡張子のファイルのみが欲しい場合は、ワイルドカードに”.{欲しい拡張子}”などと指定すればいいわけですね。

しかし、実はうまくいかない場合があります。

それは、ワイルドカードで指定した拡張子が3文字の場合です

サンプル

対象のフォルダ

前方一致するように拡張子が2~5文字のファイルを用意。

サンプルコード(C#)

using System;
using System.IO;
using System.Linq;

namespace TestGetFiles
{
    class Program
    {
        const string TARGET_PATH = "C:\\getfiles";
        static void Main(string[] args)
        {
            Output("*.xl");
            Output("*.xls");
            Output("*.xlsx");
        }

        static void Output(string searchPattern)
        {
            System.Diagnostics.Debug.WriteLine("○searchPattern:" + searchPattern);

            string[] files = null;

            System.Diagnostics.Debug.WriteLine("--GetFiles");
            files = Directory.GetFiles(TARGET_PATH, searchPattern);
            foreach (string file in files)
            {
                System.Diagnostics.Debug.WriteLine(file);
            }

            System.Diagnostics.Debug.WriteLine("--EnumerateFiles");
            files = Directory.EnumerateFiles(TARGET_PATH, searchPattern).ToArray();
            foreach (string file in files)
            {
                System.Diagnostics.Debug.WriteLine(file);
            }
            System.Diagnostics.Debug.WriteLine("");
        }
    }
}

ワイルドカードに指定する拡張子が2文字、3文字、4文字の場合それぞれで、取得したファイル一覧を出力するコードです。

実行結果

○searchPattern:*.xl
--GetFiles
C:\getfiles\test.xl
--EnumerateFiles
C:\getfiles\test.xl

○searchPattern:*.xls
--GetFiles
C:\getfiles\test.xls
C:\getfiles\test.xlsx
C:\getfiles\test.xlsxa
--EnumerateFiles
C:\getfiles\test.xls
C:\getfiles\test.xlsx
C:\getfiles\test.xlsxa

○searchPattern:*.xlsx
--GetFiles
C:\getfiles\test.xlsx
--EnumerateFiles
C:\getfiles\test.xlsx

上記の通り、指定した拡張子が2文字、4文字の場合は期待通りの結果が帰ってきていますが、3文字の場合は違和感を覚える結果となっています。

ちなみに私の環境では、実行するドライブによっても結果が異なりました。

Cドライブでやると上記のおかしな結果に、Dドライブでやると3文字でも期待通りの結果が帰ってきます…。どちらもファイルフォーマットはNTFS。

現象発生有無の違いは、8.3形式の名前生成が有効か無効かの違い。システムドライブはデフォルトで8.3形式の名前生成が有効になっているため、Cドライブでのみ発生したというカラクリ。

原因

仕様です…。Microsoftのリファレンスに以下の様に記載されています。

注意

.NET Frameworkのみ: でアスタリスクワイルドカード文字を使用し、3 文字のファイル拡張子 (“.txt” など) を指定すると、このメソッドは、指定した拡張子で始まる拡張子を持つファイルも返します。 searchPattern * たとえば、検索パターン “.xls” は、”book.xls” と * “book.xlsx” の両方を返します。 この動作は、検索パターンでアスタリスクが使用され、指定されたファイル拡張子が正確に 3 文字である場合にのみ発生します。 アスタリスクの代わりに疑問符のワイルドカード文字を使用する場合、このメソッドは、指定されたファイル拡張子と正確に一致するファイルのみを返します。 次の表に、この異常を示.NET Framework。

ディレクトリ内のファイル検索パターン.NET 5+ の戻り値.NET Framework戻り値
file.ai、file.aif*.aifile.aifile.ai
book.xls、book.xlsx*.xlsbook.xlsbook.xls、book.xlsx
file.ai、file.aif?.Aifile.aifile.ai
book.xls、book.xlsx?.xlsbook.xlsbook.xls
Directory.EnumerateFiles メソッド (System.IO) | Microsoft Docs

解決策

じゃあ3文字の拡張子のファイル一覧が欲しいときはどうするんだよ~というところですが、解決策の1つは自分でフィルタをかけることです。

サンプルコード(C#)

            System.Diagnostics.Debug.WriteLine("--MyFilter");

            //フィルタ処理
            files = Directory.GetFiles(TARGET_PATH).Where(file => file.ToLower().EndsWith(".xls")).ToArray();
            foreach (string file in files)
            {
                System.Diagnostics.Debug.WriteLine(file);
            }

実行結果

--MyFilter
C:\getfiles\test.xls

おわりに

こういうのって知ってるか知ってないかの話ですが、なかなか知るきっかけがないですよね。

やっぱりなんでも使うタイミングでリファレンスをきっちり確認しないとだめですね。

プロフィール
筆者:UMAイカ

IT企業に勤務しています。当ブログは、うダツの上がらないサラリーマンがよりよい人生を目指して購入した商品のレビューや、仕事の備忘、生活の知恵を発信します。

アーカイブ