获取具有多个扩展名的文件

104

我猜你的意思是“不止一个扩展名”……但两个也不行。 - Thomas Levesque
yeup 对于两个也不起作用。 - rd42
顺便问一下,你知道GetFiles与C#无关,只与.NET相关吗? - John Saunders
@JohnSaunders,你确定这个吗? - Vikram
1
@VikramNarkar:是的。你在C#参考中找不到它,而且它同样适用于VB.NET或任何其他CLR语言。 - John Saunders
@JohnSaunders,非常感谢! - Vikram
7个回答

79

何不创建一个扩展方法?这样更易读。

public static IEnumerable<FileInfo> GetFilesByExtensions(this DirectoryInfo dir, params string[] extensions)
{
    if (extensions == null) 
         throw new ArgumentNullException("extensions");
    IEnumerable<FileInfo> files = Enumerable.Empty<FileInfo>();
    foreach(string ext in extensions)
    {
       files = files.Concat(dir.GetFiles(ext));
    }
    return files;
}

编辑:一个更有效的版本:

public static IEnumerable<FileInfo> GetFilesByExtensions(this DirectoryInfo dir, params string[] extensions)
{
    if (extensions == null) 
         throw new ArgumentNullException("extensions");
    IEnumerable<FileInfo> files = dir.EnumerateFiles();
    return files.Where(f => extensions.Contains(f.Extension));
}

使用方法:

DirectoryInfo dInfo = new DirectoryInfo(@"c:\MyDir");
dInfo.GetFilesByExtensions(".jpg",".exe",".gif");

7
这很低效。 - SLaks
2
是的。看我的答案。此外,你应该调用SelectMany而不是Concatreturn extensions.SelectMany(dir.GetFiles); - SLaks
1
与其像上面所示那样检查扩展名,您可以使用第一个示例并将搜索模式传递到EnumerateFiles中,仍然能够对文件名进行检查,而不仅仅是检查扩展名,同时保留第二个示例的效率。 - Jeff Winn
4
为避免错过大写的文件扩展名,我会在Contains方法中添加一个StringComparer.OrdinalIgnoreCase。 return files.Where(f => extensions.Contains(f.Extension, StringComparer.OrdinalIgnoreCase)); - Doc Snuggles
1
静态类ExtensionMethods { public static FileInfo[] GetFilesByExtensions(this DirectoryInfo dir, params string[] extensions) { if (extensions == null) throw new ArgumentNullException("extensions"); IEnumerable<FileInfo> files = dir.EnumerateFiles(); return files.Where(f => extensions.Contains(f.Extension)).ToArray(); } } - Oleksii Zhyglov
显示剩余3条评论

74

你可以获取每个文件,然后筛选数组:

public static IEnumerable<FileInfo> GetFilesByExtensions(this DirectoryInfo dirInfo, params string[] extensions)
{
    var allowedExtensions = new HashSet<string>(extensions, StringComparer.OrdinalIgnoreCase);

    return dirInfo.EnumerateFiles()
                  .Where(f => allowedExtensions.Contains(f.Extension));
}

这个方法(微小地)比其他答案都要快。
在 .Net 3.5 中,用 GetFiles 替换 EnumerateFiles(后者更慢)。

使用方式如下:

var files = new DirectoryInfo(...).GetFilesByExtensions(".jpg", ".mov", ".gif", ".mp4");

这段代码无法查找子文件夹。我修改了代码,像这样。您认为正确吗? return dirInfo.EnumerateFiles(".", enumerationOptions: new EnumerationOptions { RecurseSubdirectories=true }) .Where(f => allowedExtensions.Contains(f.Extension)); - Furkan Gözükara

61

你不能这样做,因为GetFiles只接受单个搜索模式。相反,你可以不带任何模式调用GetFiles,并在代码中过滤结果:

string[] extensions = new[] { ".jpg", ".tiff", ".bmp" };

FileInfo[] files =
    dinfo.GetFiles()
         .Where(f => extensions.Contains(f.Extension.ToLower()))
         .ToArray();

如果你正在使用 .NET 4,你可以使用 EnumerateFiles 方法来避免一次性加载所有 FileInfo 对象到内存中:

string[] extensions = new[] { ".jpg", ".tiff", ".bmp" };

FileInfo[] files =
    dinfo.EnumerateFiles()
         .Where(f => extensions.Contains(f.Extension.ToLower()))
         .ToArray();

谢谢,我使用了.NET 4并得到了以下错误。我是新手,如果这是一个显而易见的修复,请原谅:错误4:“bool”不包含“ToArray”的定义,也没有接受类型为“bool”的第一个参数的扩展方法“ToArray”(您是否缺少使用指令或程序集引用?) - rd42
缺少一个闭合括号,我已经修复了。 - Thomas Levesque
没错,使用EnumerateFiles()能够获得更好的性能。 - Cheng Chen
1
使用 HashSet<string> 会更快。 - SLaks
@SLaks,也许取决于扩展数量... 对于仅有3个扩展,我认为差别不大。 - Thomas Levesque
对我有用,谢谢。 - Wessam El Mahdy

27
您可以使用LINQ Union方法:
dir.GetFiles("*.txt").Union(dir.GetFiles("*.jpg")).ToArray();

1
很棒的解决方案,完美地为我工作。我喜欢它只有一行代码,这行代码非常易读,清晰地表达了其意图,我一眼就知道它在做什么以及为什么这样做。 - Developer63
对我来说,在dir文件夹中只有一个.xlsx文件,结果却显示出了两个同名的.xlsx文件。 - Sid133
@Sid133 这怎么可能呢?Union 是在不同的扩展上执行的。 - Ievgen
@Ievgen 我不知道,我已经在这里发布了问题陈述。https://stackoverflow.com/q/72041497/9304804 - Sid133

9
以下代码可获取jpg、tiff和bmp文件,并提供一个IEnumerable<FileInfo>,您可以遍历它:
var files = dinfo.GetFiles("*.jpg")
    .Concat(dinfo.GetFiles("*.tiff"))
    .Concat(dinfo.GetFiles("*.bmp"));

如果你真的需要一个数组,只需在末尾添加.ToArray()

3
如果目录中有很多文件,那么这样做将非常低效... - Thomas Levesque
我最喜欢这个解决方案,因为它简单易读。 - Dominic Isaia

3

我不确定是否可能。MSDN GetFiles reference 中说了一个搜索模式,而不是一组搜索模式。

我可能会倾向于分别获取每个列表,并将它们“foreach”到最终列表中。


0

我知道有更优雅的方法来做这件事,我愿意听取建议...这是我所做的:

          try
            {


             // Set directory for list to be made of
                DirectoryInfo jpegInfo = new DirectoryInfo(destinationFolder);
                DirectoryInfo jpgInfo = new DirectoryInfo(destinationFolder);
                DirectoryInfo gifInfo = new DirectoryInfo(destinationFolder);
                DirectoryInfo tiffInfo = new DirectoryInfo(destinationFolder);
                DirectoryInfo bmpInfo = new DirectoryInfo(destinationFolder);

                // Set file type
                FileInfo[] Jpegs = jpegInfo.GetFiles("*.jpeg");
                FileInfo[] Jpgs = jpegInfo.GetFiles("*.jpg");
                FileInfo[] Gifs = gifInfo.GetFiles("*.gif");
                FileInfo[] Tiffs = gifInfo.GetFiles("*.tiff");
                FileInfo[] Bmps = gifInfo.GetFiles("*.bmp");

        //  listBox1.Items.Add(@"");  // Hack for the first list item no preview problem
        // Iterate through each file, displaying only the name inside the listbox...
        foreach (FileInfo file in Jpegs)
        {
                listBox1.Items.Add(file.Name);
                Photo curPhoto = new Photo();
                curPhoto.PhotoLocation = file.FullName;
                metaData.AddPhoto(curPhoto);
            }

          foreach (FileInfo file in Jpgs)
          {
              listBox1.Items.Add(file.Name);
                Photo curPhoto = new Photo();
                curPhoto.PhotoLocation = file.FullName;
                metaData.AddPhoto(curPhoto);
            }
          foreach (FileInfo file in Gifs)
          {
              listBox1.Items.Add(file.Name);
              Photo curPhoto = new Photo();
              curPhoto.PhotoLocation = file.FullName;
              metaData.AddPhoto(curPhoto);
          }
          foreach (FileInfo file in Tiffs)
          {
              listBox1.Items.Add(file.Name);
              Photo curPhoto = new Photo();
              curPhoto.PhotoLocation = file.FullName;
              metaData.AddPhoto(curPhoto);
          }
          foreach (FileInfo file in Bmps)
          {
              listBox1.Items.Add(file.Name);
              Photo curPhoto = new Photo();
              curPhoto.PhotoLocation = file.FullName;
              metaData.AddPhoto(curPhoto);
          }

你不需要为每种类型创建一个单独的DirectoryInfo实例...而且有很多重复的代码,你应该用一个方法来重构它。无论如何,我更新了我的答案来修复错误,你试过了吗? - Thomas Levesque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接