web-dev-qa-db-fra.com

Effectuer une recherche efficace dans un fichier trié

J'ai un gros fichier contenant une chaîne sur chaque ligne. J'aimerais pouvoir déterminer rapidement si une chaîne est dans le fichier. Idéalement, cela serait fait en utilisant un algorithme de type binaire chop.

Certains utilisateurs de Google ont révélé la commande look avec l'indicateur -b qui promet de localiser et d'afficher toutes les chaînes commençant par un préfixe donné à l'aide d'un algorithme de recherche binaire. Malheureusement, cela ne semble pas fonctionner correctement et renvoie des résultats nuls pour les chaînes que je connais sont dans le fichier (elles sont correctement renvoyées par l'équivalent grep search).

Est-ce que quelqu'un connaît un autre utilitaire ou une autre stratégie pour effectuer une recherche efficace dans ce fichier?

10
Matt

Il y a une différence essentielle entre grep et look:

Sauf indication contraire explicite, grep trouvera des motifs même quelque part dans les lignes. Pour look, la page de manuel indique:

look - lignes d'affichage début avec une chaîne donnée

Je n'utilise pas très souvent look, mais cela a bien fonctionné avec un exemple trivial que je viens d'essayer.

8
Klaus-Dieter Warzecha

Peut-être une réponse un peu tardive:

Sgrep va vous aider.

Sgrep (grep trié) recherche dans les fichiers d'entrée triés les lignes correspondant à une clé de recherche et génère les lignes correspondantes. Lors de la recherche de gros fichiers, sgrep est beaucoup plus rapide que grep Unix traditionnel, mais avec des restrictions importantes.

  • Tous les fichiers d'entrée doivent être des fichiers réguliers triés.
  • La clé de tri doit commencer au début de la ligne.
  • La clé de recherche ne correspond qu'au début de la ligne.
  • Aucun support d'expression régulière.

Vous pouvez télécharger la source ici: https://sourceforge.net/projects/sgrep/?source=typ_redirect

et les documents ici: http://sgrep.sourceforge.net/

Autrement:

Je ne sais pas quelle est la taille du fichier. Peut-être devriez-vous essayer parallèlement:

https://stackoverflow.com/questions/9066609/fastest-possible-grep

Je fais toujours grep avec des fichiers dont la taille> 100 Go, ça marche bien.

3
memorybox

Si vous le voulez vraiment rapide (O (1) rapide), vous pouvez construire un ensemble de hachage à examiner. Je ne pouvais pas trouver une implémentation qui me permettrait de stocker un hachage pré-construit dans un fichier et de le tester sans sans avoir à lire le fichier entier dans mémoire, donc j'ai roulé le mien .

Construire le hash set (-b/--build):

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

Analyser le hash set (-p/--probe):

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

… Ou avec une chaîne pour rechercher sur une entrée standard:

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

Vous pouvez désactiver la sortie de --probe avec l'option -q/--quiet si le statut de sortie ne vous intéresse que:

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

Pour plus d'options, voir la description d'utilisation accessible via l'option -h/--help ou le fichier README qui l'accompagne.

0
David Foerster

sgrep pourrait fonctionner pour vous:

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

La page du projet http://sgrep.sourceforge.net/ dit:

Sgrep utilise un algorithme de recherche binaire, qui est très rapide, mais nécessite une entrée triée.

Pour l'insertion cependant, je pense qu'il n'y a pas de meilleure solution que d'utiliser une base de données: https://stackoverflow.com/questions/10658380/Shell-one-liner-to-add-a-line-to -a-Sorted-File/33859372 # 33859372

Vous pouvez déchiqueter le fichier en morceaux puis grep le morceau que vous vouliez:

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

alors la recherche ressemblerait à ceci:

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

Cela fait deux choses:

  1. lire et écrire des fichiers compressés. Il est généralement plus rapide de charger le processeur (très rapidement) au lieu du disque (très lent)
  2. pour hacher des objets afin d'obtenir une distribution approximativement égale, vous pouvez utiliser un hachage plus court ou plus long afin de réduire la taille de chaque élément (mais je vous recommande d'utiliser des sous-répertoires imbriqués si vous le faites).
0
Joe