【问题标题】:Reading large files and filter using contains() java使用 contains() java 读取大文件和过滤器
【发布时间】:2016-12-29 03:59:22
【问题描述】:

我正在使用 java 中的 BufferedReader 读取大型日志文件。我必须过滤文件的内容并将数据存储在数据库中。 例子。

BufferedReader br=new BufferedReader(new FileReader("test.log"));
String line;
while((line=br.readLine())!=null){
   if(line.contains("filter1") || line.contains("filter2") ||
       line.contains("filter3") || line.contains("filter4")...){
        //creating object and storing using hibernate
    }
}

我有超过 50 个这样的过滤器,并且在读取超过 100 MB 的文件时会出现问题。匹配这些过滤字符串浪费了很多时间。

如果条件是读取的行的子字符串,我不能使用 Collection.contains(line) 作为过滤器。花费的时间不是IO,而是过滤内容和创建对象进行存储。

编辑 1 :- filter1, filter2 只是为了简单起见。在实际情况下,过滤器就像 - “新文件”、“报告”、“从文件夹中删除”、“模式”、“移动”、“复制”、“添加到队列”、“唯一 id”等。这些是我检查该行是否包含用于存储的相关数据的特定关键字。

请提出一个更好的方法来实现同样的目标。

【问题讨论】:

  • 尝试使用来自org.apache.commons.lang3.StringUtilscommons.apache.org/proper/commons-lang/apidocs/org/apache/…containsAny方法
  • 如前所述,您无能为力来改进它。如果您需要检查一行是否包含 50 个字符串之一,您可能需要从头到尾查找所有 50 个字符串。如果您对字符串的潜在位置有更多了解,或者如果字符串 A 没有出现,那么可以肯定字符串 B 没有出现等等,您可以稍微改进一下。或者您可以使用专门的方法使用 trie 进行搜索,这样您就不必扫描每个字符串的整行。

标签: java regex memory optimization filereader


【解决方案1】:

在 Java 8 中,您可以使用 Files.lines 将文件作为流读取。

本示例向您展示如何使用 Stream 过滤内容,将整个内容转换为大写并以 List 形式返回。

c://lines.txt – A simple text file for testing
line1
line2
line3
line4
line5

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TestReadFile {

    public static void main(String args[]) {

        String fileName = "c://lines.txt";
        List<String> list = new ArrayList<>();

        try (Stream<String> stream = Files.lines(Paths.get(fileName))) {

            //1. filter line 3
            //2. convert all content to upper case
            //3. convert it into a List
            list = stream
                    .filter(line -> !line.startsWith("line3"))
                    .map(String::toUpperCase)
                    .collect(Collectors.toList());

        } catch (IOException e) {
            e.printStackTrace();
        }

        list.forEach(System.out::println);

    }

}

【讨论】:

  • Java 8 中的Files.lines 将从文件中读取所有行作为字符串流,在使用流时会延迟填充。因此,如果您有一个巨大的文件并且您只读取前 100 行,那么其余的行将不会加载到内存中,这会带来更好的性能。
  • 您可以使用 java.util.stream.Streams 类中的不同方法来处理从文件中读取的行,然后再打印它们或将它们返回给调用者。这不仅仅是在 Java 8 中引入的 lambda 表达式,还有更多这样的好东西隐藏在 lambda 和流等大特性的光环后面。
  • 这里的问题不是读行。在原始程序中,行是逐行读取的,而不是预先加载的。因此,您可以获得相同的一般阅读性能。 OP 明确表示问题出在比较(过滤器)中。你的效率并不高。它甚至没有做同样的事情。
【解决方案2】:

这取决于您的过滤器的外观。如果真的是filter1filter2 等,那么你可以使用像

这样的正则表达式
private static final Pattern pattern = Pattern.compile("filter[0-9]");

... // in a loop
if (pattern.matcher(line).matches()) {...}

(你也可以避免分配)。您在这里不需要精确的过滤器,只需排除高概率的不匹配行(并且排除不匹配的行)。

例如,您可以使用 4-gram 或类似的,使用 rolling hash 类似

/// Initialization
Set<Integer> hashesOf4grams = new HashSet<>();
for (String s : filters) {
    if (s.length() < 4) {
        ... do some handling for short strings, omitted here as probably not needed.
    }
    int hash = 0;
    for (int i = 0; i < 4; ++i) {
        hash = (hash << 8) + s.charAt(i);
    }
    hashesOf4grams.add(hash);
}


/// Loop.
for (String line : lines) {
    boolean maybeMatching = false;
    int hash = 0;
    for (int i = 0; i < line.length() && !maybeMatching; ++i) {
       hash = (hash << 8) + line.charAt(i);
       maybeMatching = hashesOf4grams.contains(hash);
    }
    if (!maybeMatching) {
        continue;
    }

    // Slow test.
    boolean surelyMatching = false;
    for (String s : filters) {
        if (line.contains(s)) {
            surelyMatching = true;
            break;
        }
    }
    if (surelyMatching) {...}
}

上面的转换确保只有最后 4 个字符重要。您可以使用一些原始集合来代替Set.contains(带有拳击)。

你可以使用tries...

您也可以使用公共子字符串。您的示例仍然太短,无法提供任何有用的信息,但类似于

private static final Pattern pattern = Pattern.compile("new file|re(port|moved from folder)");

可能比单独测试所有内容更好。我想,尝试应该是最好的,但 N-gram 更简单,可以很好地工作。

在我上面的实现中,我假设所有过滤器的长度至少为 4。


【讨论】:

  • filter1,filter2 只是为了简单。
  • @AnuragUpadhyaya 请添加更多字符串。
  • 我已经更新了编辑..但是过滤器中没有固定的模式,如果那是你要找的。​​span>
  • @AnuragUpadhyaya 我明白了。查看尝试或尝试理解我写的散列。我想,两者都应该运作良好。选择一个,我会解释不清楚的地方。
猜你喜欢
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
相关资源
最近更新 更多