web-dev-qa-db-fra.com

Recherche rapide de chaînes dans un très gros fichier

Quelle est la méthode la plus rapide pour rechercher des lignes dans un fichier contenant une chaîne. J'ai un fichier contenant des chaînes à rechercher. Ce petit fichier (smallF) contient environ 50 000 lignes et ressemble à:

stringToSearch1
stringToSearch2
stringToSearch3

Je dois rechercher toutes ces chaînes dans un fichier plus grand (environ 100 millions lignes). Si une ligne de ce fichier plus volumineux contient la chaîne de recherche, la ligne est imprimée.

La meilleure méthode que j'ai trouvée jusqu'à présent est

grep -F -f smallF largeF

Mais ce n'est pas très rapide. Avec seulement 100 chaînes de recherche dans smallF, cela prend environ 4 minutes. Pour plus de 50 000 chaînes de recherche, cela prendra beaucoup de temps.

Existe-t-il une méthode plus efficace?

20
user262540

J'ai remarqué une fois que l'utilisation de -E ou plusieurs -e les paramètres sont plus rapides que l'utilisation de -f. Notez que cela peut ne pas être applicable à votre problème car vous recherchez 50 000 chaînes dans un fichier plus grand. Cependant, je voulais vous montrer ce qui peut être fait et ce qui pourrait valoir la peine d'être testé:

Voici ce que j'ai remarqué en détail:

Avoir un fichier de 1,2 Go rempli de chaînes aléatoires.

>ls -has | grep string
1,2G strings.txt

>head strings.txt
Mfzd0sf7RA664UVrBHK44cSQpLRKT6J0
Uk218A8GKRdAVOZLIykVc0b2RH1ayfAy
BmuCCPJaQGhFTIutGpVG86tlanW8c9Pa
etrulbGONKT3pact1SHg2ipcCr7TZ9jc
.....

Maintenant, je veux rechercher les chaînes "ab", "cd" et "ef" en utilisant différentes approches grep:

  1. En utilisant grep sans indicateurs, recherchez un à la fois:

    grep "ab" strings.txt> m1.out
    2,76s utilisateur 0,42s système 96% cpu 3,313 total

    grep "cd" strings.txt >> m1.out
    2,82s utilisateur 0,36s système 95% cpu 3,322 au total

    grep "ef" strings.txt >> m1.out
    2,78s utilisateur 0,36s système 94% cpu 3,360 total

Donc, au total, la recherche prend presque 10 secondes .

  1. Utilisation de grep avec -f drapeau avec des chaînes de recherche dans search.txt

    >cat search.txt
     ab
     cd
     ef
    
    >grep -F -f search.txt strings.txt > m2.out  
    31,55s user 0,60s system 99% cpu 32,343 total
    

Pour certaines raisons, cela prend presque 32 secondes .

  1. Maintenant, en utilisant plusieurs modèles de recherche avec -e

    grep -E "ab|cd|ef" strings.txt > m3.out  
    3,80s user 0,36s system 98% cpu 4,220 total
    

    ou

    grep --color=auto -e "ab" -e "cd" -e "ef" strings.txt > /dev/null  
    3,86s user 0,38s system 98% cpu 4,323 total
    

La troisième méthode utilisant -E n'a pris 4,22 secondes que pour parcourir le fichier.

Vérifions maintenant si les résultats sont les mêmes:

cat m1.out | sort | uniq > m1.sort  
cat m3.out | sort | uniq > m3.sort
diff m1.sort m3.sort
#

Le diff ne produit aucune sortie, ce qui signifie que les résultats trouvés sont les mêmes.

Peut-être que vous voulez essayer, sinon je vous conseillerais de regarder le fil "grep le plus rapide possible", voir le commentaire de Cyrus.

17
cb0

Vous voudrez peut-être essayer tamiser ou ag . Sift en particulier répertorie des références assez impressionnantes par rapport à grep.

1
ajfabbri

Remarque: je me rends compte que ce qui suit n'est pas une solution basée sur bash, mais étant donné votre grand espace de recherche, une solution parallèle est garantie.


Si votre machine a plus d'un cœur/processeur, vous pouvez appeler la fonction suivante dans Pythran , pour paralléliser la recherche:

#!/usr/bin/env python

#pythran export search_in_file(string, string)
def search_in_file(long_file_path, short_file_path):
    _long = open(long_file_path, "r")

    #omp parallel for schedule(guided)
    for _string in open(short_file_path, "r"):
        if _string in _long:
            print(_string)

if __name__ == "__main__":
    search_in_file("long_file_path", "short_file_path")

Remarque: Dans les coulisses, Pythran prend Python et tente de le compiler agressivement en C++ très rapide.

1
boardrider