Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 查找大型文本文件中出现的模式(当前使用Aho Corasick)_Java_Search_Pattern Matching_Trie_Aho Corasick - Fatal编程技术网

Java 查找大型文本文件中出现的模式(当前使用Aho Corasick)

Java 查找大型文本文件中出现的模式(当前使用Aho Corasick),java,search,pattern-matching,trie,aho-corasick,Java,Search,Pattern Matching,Trie,Aho Corasick,我有一个大的文本(5MB-500MB)文件和一组几千个模式。对于每个模式,我希望获得该模式在文件中的出现次数。文本不包含空格,是一个基本的长字母数字字符串 为了这个目的,我试着使用Aho-Corasick算法, 特别是Robert Bor的Java实现,它确实运行得足够快,但存在一个问题:将模式中的发射计数为字符串的结果不等于使用文本编辑器(如notepad++)打开文本文件并计数模式的结果。对我来说,重要的是,计数的出现次数将恰好是在文件中找到模式的次数。因此,我需要找到解决这个问题的办法 为

我有一个大的文本(5MB-500MB)文件和一组几千个模式。对于每个模式,我希望获得该模式在文件中的出现次数。文本不包含空格,是一个基本的长字母数字字符串

为了这个目的,我试着使用Aho-Corasick算法, 特别是Robert Bor的Java实现,它确实运行得足够快,但存在一个问题:将模式中的发射计数为字符串的结果不等于使用文本编辑器(如notepad++)打开文本文件并计数模式的结果。对我来说,重要的是,计数的出现次数将恰好是在文件中找到模式的次数。因此,我需要找到解决这个问题的办法

为了实现我的目标,我是否可以在算法的实现中进行更改?也许一个类似的处理程序能解决我的问题? 我也愿意接受其他建议,例如替换算法/解决方案。然而,如果可能的话,我希望继续使用java,并尽可能快地获得结果(例如,emits索引对我来说并不重要)


编辑:例如,即使是安装文件的以下小文本: ,以及模式:

5B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B4E5FF55B5FF

根据发射计数,它在文件中显示150次,但根据Notepad++/Ctrl-f在浏览器中的计数功能,它仅显示10次

同一文本中的另一个示例:

F34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0FF34A6E0

根据发射计数显示99次,但根据文本编辑器的计数仅显示10次

链接到算法的实现。 我当前基于实现运行的内容:

  Trie trie = Trie.builder().addKeywords(wordsMap.keySet())
                        .build();
    Collection<Emit> ls2 = trie.parseText(str);``
            for (Emit e: ls2) {
                if (!map.containsKey(e.getKeyword()))
                      map.put(e.getKeyword(),1);
                else {
                    int val = map.get(e.getKeyword());
                    map.replace(e.getKeyword(),val+1);
                }
            }
            return map;
Trie-Trie=Trie.builder().addKeywords(wordsMap.keySet())
.build();
集合ls2=trie.parseText(str)``
对于(发射e:ls2){
如果(!map.containsKey(e.getKeyword()))
put(例如getKeyword(),1);
否则{
int val=map.get(如getKeyword());
map.replace(e.getKeyword(),val+1);
}
}
返回图;
谢谢


我还尝试了实现中可用的非重叠选项,但它不符合要求,而且对我的使用来说太慢

首先,当使用
ignoreOverlaps()
构建
Trie
时,不清楚该算法在正确性方面如何或为什么不能满足您的需要。不过,我相信你的话。当你说在这种情况下会对性能产生影响时,我也愿意相信你

因此,与其深入研究算法的实现,不如将其与重叠一起使用,然后手动删除重叠。在这种情况下,我认为您将能够微调要跳过的发射

下面是初始化
Trie
的代码:

String text = ... // read the text somewhere

Set<String> keywords = new HashSet<>();
keywords.add("keyword1");
keywords.add("keyword2");

Trie trie = Trie.builder().addKeywords(keywords).build(); // with overlaps!
这种方法使用自定义收集器来计算每个关键字的非重叠发射数。在不使用自定义收集器的情况下,还有其他更简单的方法可以做到这一点,但它们需要为每个关键字保留一个非重叠发射的列表。因为你只需要计数,而且你正在处理2000个关键词和一个巨大的文本,我认为这种方式更好

收集器基本上会跟踪收集的最后一个非重叠发射,并且仅当当前发射不与最后一个非重叠发射重叠时,才会增加正在收集的当前发射的计数。此外,它仅适用于顺序流


注意:如果需要在计数器递增时进行微调,可以自定义
Acc
本地类的
add
方法。

@FedericoPeraltaSchaffner,added!请告诉我,如果为了完全理解这个问题,还缺少一些东西,我是否可以补充一些东西来帮助您。@FedericoPeraltaSchaffner修复了下载链接。根据我使用的文件上传服务,它应该在接下来的30天内可用。@Federicoperaltachaffner我认为是这样的,但我也尝试了算法的非重叠选项,仍然得到了有问题的结果。也许是因为第二条规则:“最左派压倒最右派”。因此,我想知道我能做的最小的改变是什么,它会给我想要的结果。谢谢@Federicoperaltachaffner是一个不重叠的计数,比如文本编辑器计数,而不是我目前使用的算法实现中内置的不重叠机制。如果需要,我不介意切换算法。我只想统计给定文本中出现的大(长度>=100)模式。尽可能使用Java。@Federicoperaltachaffner我第一次从你那里听说Trie,从来没有机会深潜:(你应该把它作为一个答案发布出来,谢谢!我明天会试用,看看是否能让它工作。似乎比实现中提供的非重叠解决方案工作得更好。非常感谢所有的帮助!
Collection<Emit> parseResults = trie.parseText(text);
Map<String, Long> map = parseResults.stream()
    .collect(Collectors.groupingBy(Emit::getKeyword, countingNonOverlaps()));
private static Collector<Emit, ?, Long> countingNonOverlaps() {

    class Acc {
        Emit last;
        long count = 0;

        void add(Emit e) {
            if (last == null || !last.overlapsWith(e)) { count++; last = e; }
        }

        Acc merge(Acc another) {
            throw new UnsupportedOperationException("Parallel not supported");
        }
    }
    return Collector.of(Acc::new, Acc::add, Acc::merge, acc -> acc.count);
}