【问题标题】:How to efficiently test if files with a matching filename (regex or wildcard) exists in a directory?如何有效地测试目录中是否存在具有匹配文件名(正则表达式或通配符)的文件?
【发布时间】:2011-11-01 18:44:48
【问题描述】:

我正在寻找一种有效的方法来测试是否存在具有特定模式的文件名的文件。

使用通配符的示例:

  • ????.*
  • ???????.*
  • *.png
  • *.jpg

使用正则表达式的示例:

  • [012]{4}.*
  • [012]{7}.*

问题是我必须测试的目录包含多达 500.000 个文件。 我知道执行此类测试的唯一方法是使用 File 类的方法:

String[] list()
String[] list(FilenameFilter filter)
File[] listFiles()
File[] listFiles(FileFilter filter)
File[] listFiles(FilenameFilter filter)

问题在于它们基本上都是以相同的方式实现的:首先调用 list() 来获取所有可用文件,然后对其应用过滤器。

请想象一下,如果我们想将它应用于包含 500.000 个文件的文件夹会发生什么......

如果 Java 中有任何替代方法可以检索有关目录中文件的第一个匹配文件的文件名,而不必枚举所有文件?

如果 JNI 是唯一的选择 - 是否有一个库可以为六个主要平台(Linux、Windows 和 OSX 各 32 位和 64 位)提供预编译的二进制文件?

【问题讨论】:

  • 我认为您对此无能为力。也许您可以随机访问文件?如果您只是在寻找与特定模式匹配的文件,这可能会产生更好的结果。

标签: java regex file find wildcard


【解决方案1】:

我认为你很困惑。据我所知,目前没有操作系统支持在其文件系统界面中列出/搜索模式。所有支持模式的实用程序都通过列出目录(例如,在 POSIX 系统上使用 readdir())然后执行字符串匹配来实现。

因此,在 Java 或任何其他语言中没有通用的低级方法可以更有效地做到这一点。也就是说,您至少应该调查以下方法:

  • 确保您只检索文件名,并且您不会探测文件节点本身以获取其他元数据(例如它们的大小),因为这会导致每个文件的额外操作。

    李>
  • 检索文件列表一次并将其缓存,可能与更新的文件系统事件通知接口相关联(例如JNotifyJava 7 WatchService interface)。

编辑:

我查看了我的 Java 实现。 File 类的方法中唯一明显的缺点是,一旦找到匹配项,列出目录就不会停止。但是,这仅在您只执行一次搜索时才重要 - 否则缓存完整目录列表仍然会更有效率。

如果您可以使用相对较新的 Java 版本,您可能想看看似乎没有相同弱点的 Java NIO 类(12)。

【讨论】:

  • 新的DirectoryStream界面看起来非常漂亮。感谢您指出这一点。
【解决方案2】:

这在我的机器上大约需要 1 分钟(有点旧)

import java.io.*;
import java.util.*;
import java.util.regex.*;
public class Main {
    static void match(File dir, Pattern pattern, List<File> matching) {
        File[] files = dir.listFiles();
        if(files==null) {
            System.out.println(dir + " is strange!");
            return;
        }
        for (File file : files)
            if (file.isDirectory()) match(file, pattern, matching);
            else if (file.isFile()) {
                Matcher matcher = pattern.matcher(file.getName());
                if (matcher.matches()) {
                    matching.add(file);
                    //System.out.println(file + "************");
                }
            }
    }
    static void makeFiles(File dir,int n) throws IOException {
        for(int i=0;i<n;i++) {
            File file=new File(dir,i+".foo");
            FileWriter fw=new FileWriter(file);
            fw.write(1);
            fw.close();
        }
    }
    public static void main(String[] args) throws IOException {
        File dir = new File("data");
        final int n=500000;
        //makeFiles(dir,n);
        long t0=System.currentTimeMillis();
        Pattern pattern = Pattern.compile(".*\\.foo");
        List<File> matching = new LinkedList<File>();
        match(dir, pattern, matching);
        long t1=System.currentTimeMillis();
        System.out.println("found: "+matching.size());
        System.out.println("elapsed time: "+(t1-t0)/1000.);
        System.out.println("files/second: "+n/((t1-t0)/1000.));
    }
}

【讨论】:

  • 编写测试程序 - 好主意。我修改了上面的代码以获得更多细节。没有刷新的 OS-file-cache 执行时间大约需要 120 秒(core2Duo 和 3.5" 300GB HDD),但调用 dir.listFiles() 本身只需要 8 秒。顺便说一句:列出 500000 个文件需要大约 80MB 的堆内存。
【解决方案3】:

我认为你是在本末倒置。

  1. 正如 Knuth 所说,过早优化是万恶之源。您是否尝试过使用 FileFilter 方法并发现它对应用程序来说太慢了?

  2. 为什么一个文件夹中有这么多文件?也许更有益的方法是以某种方式拆分这些文件,而不是将它们全部放在一个文件夹中。

【讨论】:

  • 不幸的是,我对正在阅读的文件结构没有影响。它是由其他程序生成的,而我的程序仅用于读取和处理这些文件。
  • @Robert:这仍然是第一个问题。您确定这是您的应用程序的问题吗?您是否进行了分析以检查这是否是瓶颈?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-29
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多