Patrones para encontrar ocurrencias en un archivo grande de texto (en la actualidad con Aho-Corasick)

Ron.K:

Tengo un archivo de texto grande (5 MB-500 MB) y un conjunto de unos pocos miles de patrones. Para cada patrón, quiero obtener el número de ocurrencias del patrón en el archivo. El texto no contiene espacios en blanco y es una cadena alfanumérica larga básica.

A tal fin, yo estaba tratando de utilizar el algoritmo de búsqueda de cadenas aho-corasick, específicamente implementación Java de Robert-Bor, y de hecho funciona lo suficientemente rápido, pero hay un problema: el resultado del recuento de las emite con el patrón como su cadena no es igual con el resultado de abrir el archivo de texto con un editor de texto como notepad ++ y contando el patrón. Es importante para mí que el número de ocurrencias contó será exactamente el número de veces que el patrón que se encuentra en el archivo. Por lo tanto, tengo que encontrar una solución a este problema.

¿Hay un cambio que puedo hacer en la implementación del algoritmo con el fin de cumplir con mi meta? Tal vez una EmitHandler de algún tipo va a resolver mi problema? También estoy abierto a otras sugerencias, tales como la sustitución del método algoritmo / solución. Sin embargo, me quiero quedar con Java, si es posible, y para obtener los resultados tan rápido como sea posible (los índices Emite no son importantes para mí, por ejemplo).


Editar: Por ejemplo, incluso la pequeña texto siguiente de un archivo de instalación: Archivo Enlace , y el patrón:

5b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff55b4e5ff

que de acuerdo con el recuento emite aparece 150 veces en el archivo, pero sólo aparece 10 veces de acuerdo a la función de recuento de Notepad ++ / Ctrl-F en un navegador.

Y otro ejemplo en el mismo texto:

f34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0ff34a6e0

aparece 99 veces, según la emite contar, pero sólo 10 veces, según la cuenta de un editor de texto.

Vincular a la aplicación del algoritmo, aquí . Lo que se ejecutan actualmente en base a la aplicación:

  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;

¡Gracias!

También he probado la opción de que no se solapan disponibles a la ejecución, pero no se ajusta a las necesidades y también demasiado lento para mis usos.

Federico Peralta Schaffner:

En primer lugar, no está claro cómo o por qué el algoritmo no satisface sus necesidades con respecto a la corrección cuando el Triese construye con ignoreOverlaps(). Me tomo la palabra en esto, sin embargo. También estoy dispuesto a creer que cuando se dice que hay un impacto en el rendimiento en este caso.

Así, en lugar de la excavación en la implementación del algoritmo, que haría uso mejor con superposiciones, y luego retire los solapamientos manualmente. En este caso, creo que usted será capaz de poner a punto que emite para saltar.

Aquí está el código para inicializar el 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!

Ahora, vamos a analizar el texto:

Collection<Emit> parseResults = trie.parseText(text);

Por lo que yo puedo decir, los resultados de análisis sintáctico se devuelven en orden de aparición en el texto, pero no he probado esto a fondo. Para el siguiente código para que funcione correctamente, es necesario que emite a ser ordenados por índice inicial.

Dado que emite están ordenados por índice de inicio, aquí está el código para contar emite no se solapan por palabra clave:

Map<String, Long> map = parseResults.stream()
    .collect(Collectors.groupingBy(Emit::getKeyword, countingNonOverlaps()));

Cuando el countingNonOverlaps()método de utilidad es la siguiente:

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);
}

Este enfoque utiliza un colector a medida que contar emite no se solapan por palabra clave. Hay otras maneras más simples de hacer esto sin un colector de costumbre, sino que necesitan para mantener una lista de los que no se solapan emite por palabra clave. Ya que sólo necesita los conteos y como se trabaja con 2000 palabras clave y un texto enorme, creo que de esta manera es mejor.

El colector, básicamente, no pierde de vista el último emita la no superposición de recogida y se incrementa un recuento de la corriente que se emiten recogen sólo si no se solapa con el último emiten no se solapan. Además, sólo funciona para los flujos secuenciales.

Nota: si necesita ajustar cuando se incrementa el contador, se puede personalizar el addmétodo de la Accclase local.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=212742&siteId=1
Recomendado
Clasificación