【问题标题】:How to quickly search a large file for a String in Java?如何在 Java 中快速搜索大文件中的字符串?
【发布时间】:2016-08-23 08:49:34
【问题描述】:

我正在尝试使用以下内容在大型文本文件 (400MB) 中搜索特定字符串:

File file = new File("fileName.txt");
try {
    int count = 0;
    Scanner scanner = new Scanner(file);
    while(scanner.hasNextLine()) {
        if(scanner.nextLine().contains("particularString")) {
            count++;
            System.out.println("Number of instances of String: " + count);
        }
    }
} catch (FileNotFoundException e){
    System.out.println(e);
}

这适用于小文件,但对于这个特定文件和其他大文件来说,它需要的时间太长(>10 分钟)。

最快、最有效的方法是什么?

我现在已更改为以下内容,并在几秒钟内完成 -

try {
        int count = 0;
        FileReader fileIn = new FileReader(file);
        BufferedReader reader = new BufferedReader(fileIn);
        String line;
        while((line = reader.readLine()) != null) {
            if((line.contains("particularString"))) {
                count++;
                System.out.println("Number of instances of String " + count);
            }
        }
    }catch (IOException e){
        System.out.println(e);
    }

【问题讨论】:

  • grep -c particularString fileName.txt比较速度。
  • 如果他先把整个文件读入内存不是更快吗?
  • 与您的文件访问方法无关的一件非常琐碎的事情是System.out.println 调用:如果您有大量匹配项,它实际上减慢您的执行速度,当您每次都在构建和打印一个新的String 时。当然,这不是您在这里寻找的真正优化。
  • @membersound 并行读取?你不会受到磁盘IO的限制吗?

标签: java io java.util.scanner


【解决方案1】:

首先要弄清楚您实际阅读整个文件的内容需要多长时间与扫描它们以找到您的模式需要多长时间。

如果您的结果受读取时间支配(并且假设您正确读取了它,那么通道或至少缓冲的读取器)没有太多可做的。

如果扫描时间占主导地位,您可以读取所有行,然后将要搜索的小批量行发送到工作队列中,在那里您可以让多个线程拾取行批次并在其中进行搜索。

球场数据

  • 假设硬盘读取速度为 50 MB/秒(按照现代标准,这很慢),您应该能够在
  • 查看 MD5 哈希速度基准(例如 here)向我们展示了哈希率至少可以与磁盘读取速度一样快(通常更快)。此外,字符串搜索比散列更快、更简单且并行性更好。

鉴于这 2 个估计,我认为适当的实施可以轻松地使您的运行时间达到 10 秒左右(如果您在读取行批处理时开始执行搜索作业),并且很大程度上取决于您的磁盘读取时间。

【讨论】:

  • 很好的答案,我认为很多人会更倾向于只实现批处理并期望它超级快,但实际上速度慢可能来自其他原因。
  • 谢谢。我已更改为缓冲阅读器,它已经完成了这项工作,现在需要几秒钟。
【解决方案2】:

扫描仪在这种情况下根本没有用。在底层,它会进行各种输入解析、检查、缓存等等。如果您的案例只是“遍历文件的所有行”,请使用基于简单 BufferedReader 的内容。

在您的特定情况下,我建议使用 Files.lines。

例子:

  long count = Files.lines(Paths.get("testfile.txt"))
     .filter(s -> s.contains("particularString"))
     .count();
  System.out.println(count);

(请注意,流 api 的这种特殊情况可能不涵盖您实际尝试实现的目标 - 不幸的是,您的问题并未表明该方法的结果应该是什么。)

在我的系统上,我使用 Files.lines() 或缓冲阅读器获得了大约 15% 的 Scanner 运行时间。

【讨论】:

    【解决方案3】:

    使用来自 Scanner 对象的方法 - FindWithinHorizo​​n。 Scanner 将在内部创建一个 FileChannel 来读取文件。对于模式匹配,最终将使用 Boyer-Moore 算法进行有效的字符串搜索。

    【讨论】:

      猜你喜欢
      • 2016-10-08
      • 2014-09-19
      • 2020-04-09
      • 1970-01-01
      • 2011-03-01
      • 2013-01-20
      • 2013-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多