使用contains()java

时间:2016-12-28 07:55:03

标签: java regex memory optimization filereader

我正在使用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仅为简单起见。在实际情况下,过滤器就像 - "新文件","报告","从文件夹",#34;架构",& #34;移动","复制","添加到队列","唯一ID"这些是我检查的特定关键字,以查看该行是否包含用于存储的相关数据。

请建议更好的方法来实现同样的目标。

2 个答案:

答案 0 :(得分:1)

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

此示例显示如何使用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);

    }

}

答案 1 :(得分:0)

这取决于过滤器的外观。如果确实是filter1filter2等,那么您可以使用正则表达式

private static final Pattern pattern = Pattern.compile("filter[0-9]");

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

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

例如,您可以使用4克或类似物,使用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。