Busque eficientemente el archivo ordenado


12

Tengo un archivo grande que contiene una cadena en cada línea. Me gustaría poder determinar rápidamente si hay una cadena en el archivo. Idealmente, esto se haría utilizando un algoritmo de corte binario.

Algunos Google revelaron el lookcomando con la -bbandera que promete localizar y generar todas las cadenas que comienzan con un prefijo dado usando un algoritmo de búsqueda binario. Desafortunadamente, no parece funcionar correctamente y devuelve resultados nulos para las cadenas que sé que están en el archivo (se devuelven correctamente mediante la grepbúsqueda equivalente ).

¿Alguien sabe de otra utilidad o estrategia para buscar este archivo de manera eficiente?


La respuesta principal indica la clasificación incorrecta: el hecho es que debe ordenar con: LC_COLLATE = C sort -d para que el lookcomando funcione correctamente, porque la apariencia parece ignorar la configuración regional y solo usa C como la ordenación codificada, también abrí un error debido a este comportamiento confuso: bugzilla.kernel.org/show_bug.cgi?id=198011
Sur3

look -bfalló para mí con un error File too large. Creo que está tratando de leer todo en la memoria.
Brian Minton

Respuestas:


9

Hay una diferencia esencial entre grepy look:

A menos que se indique explícitamente lo contrario, grepencontrará patrones incluso en algún lugar dentro de las líneas. Para looklos estados de la página de manual:

look: muestra líneas que comienzan con una cadena dada

No estoy usando lookmuy a menudo, pero funcionó bien en un ejemplo trivial que acabo de probar.


1
El archivo que necesito buscar tiene alrededor de 110,000,000 líneas. Si lo hago egrep "^TEST" sortedlist.txt | wc -l , obtengo 41,289 resultados. Sin embargo, los lookcomandos equivalentes look -b TEST sortedlist.txt | wc -lsolo arrojan resultados de 1995. Casi me pregunto si hay un error look.
Matt

1
@Matt Quizás lookestá usando diferentes configuraciones de clasificación que el programa que usó para ordenar el archivo.
Kasperd

4

Tal vez una pequeña respuesta tardía:

Sgrep te ayudará.

Sgrep (grep ordenado) busca en los archivos de entrada ordenados las líneas que coinciden con una clave de búsqueda y genera las líneas coincidentes. Al buscar archivos grandes, sgrep es mucho más rápido que el grep tradicional de Unix, pero con restricciones significativas.

  • Todos los archivos de entrada deben clasificarse como archivos normales.
  • La clave de clasificación debe comenzar al principio de la línea.
  • La clave de búsqueda solo coincide al principio de la línea.
  • No hay soporte para expresiones regulares.

Puede descargar la fuente aquí: https://sourceforge.net/projects/sgrep/?source=typ_redirect

y los documentos aquí: http://sgrep.sourceforge.net/

De otra manera:

No sé qué tan grande es el archivo. Quizás deberías intentarlo en paralelo:

/programming/9066609/fastest-possible-grep

Siempre hago grep con archivos de tamaño> 100 GB, funciona bien.


2
¿No es eso ya en askubuntu.com/a/701237/158442 ?
muru

sí,
completo el

Si eso es todo, debe editar esa publicación en lugar de publicar una nueva respuesta.
muru

esa publicación recomendada: sudo apt-get install sgrep para obtener sgrep, el sgrep en los repositorios de buntu no es realmente este sgrep, no estoy seguro de que sea lo mismo.
memorybox

0

Puede trocear el archivo en pedazos y luego grep solo la pieza que desea:

for line in $(cat /usr/share/dict/american-english | tr '[:upper:]' '[:lower:]' | sort | uniq)
do
    prefix=$(echo $line | md5sum - | cut -c 1-2)
    mkdir -p $prefix
    echo $line | gzip >> $prefix/subwords
done

entonces la búsqueda se vería así:

    prefix=$(echo $word | md5sum - | cut -c 1-2)
    zgrep -m 1 -w word $prefix/subwords

Esto hace dos cosas:

  1. Leer y escribir archivos comprimidos. Generalmente es más rápido poner la carga en la CPU (muy rápido) en lugar del disco (muy lento)
  2. hash cosas para obtener una distribución aproximadamente igual, puede usar un hash más corto o más largo como desee para reducir el tamaño de cada pieza (pero recomendaría usar subdirecciones anidadas si lo hace)

0

sgrep podría funcionar para usted:

sudo apt-get install sgrep
sgrep -l '"needle"' haystack.txt

La página del proyecto http://sgrep.sourceforge.net/ dice:

Sgrep utiliza un algoritmo de búsqueda binario, que es muy rápido, pero requiere una entrada ordenada.

Sin embargo, para la inserción, creo que no hay mejor solución que usar una base de datos: /programming/10658380/shell-one-liner-to-add-a-line-to-a-sorted-file/ 33859372 # 33859372


3
En sgreplos repositorios de Ubuntu es en realidad este sgrep , que está diseñado para "buscar un archivo para un patrón estructurado" y no tiene nada que ver con la búsqueda binaria.
ingomueller.net

0

Si lo desea realmente rápido (O (1) rápido), puede crear un conjunto de hash para investigar. No pude encontrar una implementación que me permitiera almacenar un hash precompilado en un archivo y probarlo sin tener que leer todo el archivo en la memoria, así que hice el mío .

Construya el conjunto de hash ( -b/ --build):

./hashset.py --build string-list.txt strings.pyhashset

Pruebe el conjunto de hash ( -p/ --probe):

./hashset.py --probe strings.pyhashset \
    'Is this string in my string list?' 'What about this one?'

... o con una cadena para buscar en la entrada estándar:

printf '%s\n' 'Is this string in my string list?' 'What about this one?' |
./hashset.py --probe strings.pyhashset

Puede silenciar la salida de --probecon la opción -q/ --quietsi solo está interesado en el estado de salida:

if ./hashset.py --quiet --probe strings.pyhashset ...; then
    echo 'Found'
else
    echo 'Not found'
fi

Para obtener más opciones, consulte la descripción de uso accesible a través de la opción -h/ --helpo el READMEarchivo adjunto .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.