Grep et fichier motifs ?
Résolu/Fermé
mortelrdv
-
22 févr. 2012 à 11:44
dubcek Messages postés 18752 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 3 octobre 2024 - 24 févr. 2012 à 15:27
dubcek Messages postés 18752 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 3 octobre 2024 - 24 févr. 2012 à 15:27
A voir également:
- Grep et fichier motifs ?
- Fichier rar - Guide
- Fichier host - Guide
- Comment ouvrir un fichier epub ? - Guide
- Comment réduire la taille d'un fichier - Guide
- Fichier iso - Guide
9 réponses
mamiemando
Messages postés
33333
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
31 octobre 2024
7 800
Modifié par mamiemando le 23/02/2012 à 00:57
Modifié par mamiemando le 23/02/2012 à 00:57
il se trouve que je souhaite savoir quels motifs dans le fichier fichier_motif qui pourraient fournir aucune résultat.
Je n'ai pas compris la question. Est-ce que la question est plutôt : parmi tous les motifs listés dans fichier_motif, quels motifs ne sont jamais retrouvés dans fichier_donnees.
Si c'est le cas :
Exemple :
Bonne chance
Je n'ai pas compris la question. Est-ce que la question est plutôt : parmi tous les motifs listés dans fichier_motif, quels motifs ne sont jamais retrouvés dans fichier_donnees.
Si c'est le cas :
for motif in $(cat fichier_motif); do (grep -q $motif fichier_donnees || echo $motif); done
Exemple :
(mando@aldur) (~) $ cat fichier_motif aaa bbb ggg (mando@aldur) (~) $ cat fichier_donnees aaabbb ccaaadd bbbddd dddcc (mando@aldur) (~) $ for motif in $(cat fichier_motif); do (grep -q $motif fichier_donnees || echo $motif); done ggg
Bonne chance
dubcek
Messages postés
18752
Date d'inscription
lundi 15 janvier 2007
Statut
Contributeur
Dernière intervention
3 octobre 2024
5 619
Modifié par mamiemando le 24/02/2012 à 00:19
Modifié par mamiemando le 24/02/2012 à 00:19
hello
essayez avec awk
essayez avec awk
$ cat fichier_motif aa bb cc dd $ cat fichier_donnees aaaaa xxxxxxx dddd $ $ awk 'NR==FNR {m[n++]=$0;next} {for(x=0;x<n;x++)if($0 ~ m[x])t[x]++} END {for(x=0;x<n;x++)if(!t[x])print m[x]}' fichier_motif fichier_donnees bb cc $
mamiemando
Messages postés
33333
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
31 octobre 2024
7 800
Modifié par mamiemando le 24/02/2012 à 00:20
Modifié par mamiemando le 24/02/2012 à 00:20
Même si la solution de dubcek marche, je ne pense pas que ça change forcément grand chose, car pour chaque ligne, on compare avec l'ensemble des motifs. La boucle for que j'ai écrite en shell raisonne pour chaque motif, puis itère sur tout le fichier, mais globalement on a toujours autant d'itérations (le nombre de ligne du premier fichier fois le nombre de ligne du second fichier). Cela veut dire qu'a priori les deux algorithmes offrent des performances comparable car la complexité est la même.
Première optimisation (simple) :
Pour améliorer les performances de cet algorithme, on pourrait commencer par marquer les motifs déjà trouvés une fois pour éviter de les rechercher à nouveau. La complexité dans le pire cas reste la même (si les motifs à trouver sont en fin de fichiers), mais en moyenne elle est meilleure.
Seconde optimisation (abordable si tu sais un peu développer) :
On peut avoir un algorithme de complexité linéaire mais cela réclame un peu de programmation. Dans l'idée on peut voir un motif comme un automate. Pour simplifier le principe je vais me limiter au cas où les motifs sont des sous-chaînes de caractères et pas des expressions rationnelles (mais dans le cas d'une expression rationnelle, la démarche reste la même).
Supposons que je cherche la chaîne "bonjour" et la chaîne "hello". Alors je peux construire deux automates :
J'ai donc ici deux automates. Je note
- ^: l'état initial : pour le moment aucun caractère du motif n'a été lu
- $: l'état final : si j'atteins cet état, j'ai trouvé le motif avec succès.
D'un point de vue implémentation, si on se limite aux sous-chaînes, il suffit de stocker un tableau de caractère par motif et de retenir l'index du caractère courant (c'est plus ou moins l'algorithme KMR appliqué à un ensemble de motifs).
Par exemple si j'indexe à partir de 0 et si j'ai lu successivement "bon", je sais que l'état courant du premier automate est 3 (je suis placé sur le n) et 0 pour le second (je suis placé sur ^). Si à l'itération suivante je lis le caractère 'z' par exemple, ceci interrompt la progression dans le premier automate et provoque un retour à l'état 0.
Plus généralement : je lis caractère par caractère le fichier de données
- Si le caractère me permet d'avancer dans l'automate, je le fais. Si j'atteins alors la fin de l'automate ($), alors le motif a été trouvé et je peux marquer ce motif comme trouvé. Je peux alors supprimer cet automate de mon ensemble d'automates à évaluer pour éviter d'avoir à faire des calculs par la suite dessus.
- Si le caractère ne me permet pas d'avancer dans un automate donné, alors je me replace sur l'état ^.
À la fin, je retourne tous les motifs qui n'ont pas été marqués (ils n'ont jamais été trouvés).
Plusieurs avantages à cette approche :
- on élimine les motifs traités (le pire cas étant comme d'habitude, les motifs que l'on cherche sont à la fin du fichier de données)
- on ne lit qu'une seule fois chaque ligne du fichier de données
- au lieu de marquer un motif (avec un boolean qui décrit s'il a été vu ou non), on peut maintenir un compteur ce qui permet de compter combien de fois il a été observer sans que cela impacte les performances de l'algorithme
- l'algorithme reste vrai pour des automates plus compliqués comme ceux engendrés par des expressions rationnelles
Troisième optimisation (sous certaines hypothèses et si tu sais programmer) :
Supposons maintenant qu'on se limite à la recherche de sous chaînes (donc on ne supporte pas les expressions rationnelles). Si les motifs à trouver sont "longs" et se ressemblent, on doit pouvoir encore un peu améliorer les performances en stockant les motifs de manières un peu plus subtile. Par exemple "bonjour" et "bonhomme". On peut alors stocker dans un arbre l'ensemble des noeuds.
L'idée consiste ensuite à marquer le(s) noeud(s) courant(s) de l'arbre et tronquer les branches qui ne servent plus quand on atteint un état final.
Bonne chance
Première optimisation (simple) :
Pour améliorer les performances de cet algorithme, on pourrait commencer par marquer les motifs déjà trouvés une fois pour éviter de les rechercher à nouveau. La complexité dans le pire cas reste la même (si les motifs à trouver sont en fin de fichiers), mais en moyenne elle est meilleure.
Seconde optimisation (abordable si tu sais un peu développer) :
On peut avoir un algorithme de complexité linéaire mais cela réclame un peu de programmation. Dans l'idée on peut voir un motif comme un automate. Pour simplifier le principe je vais me limiter au cas où les motifs sont des sous-chaînes de caractères et pas des expressions rationnelles (mais dans le cas d'une expression rationnelle, la démarche reste la même).
Supposons que je cherche la chaîne "bonjour" et la chaîne "hello". Alors je peux construire deux automates :
^ -> b -> o -> n -> j -> o -> u -> r -> $ ^ -> h -> e -> l -> l -> o -> $
J'ai donc ici deux automates. Je note
- ^: l'état initial : pour le moment aucun caractère du motif n'a été lu
- $: l'état final : si j'atteins cet état, j'ai trouvé le motif avec succès.
D'un point de vue implémentation, si on se limite aux sous-chaînes, il suffit de stocker un tableau de caractère par motif et de retenir l'index du caractère courant (c'est plus ou moins l'algorithme KMR appliqué à un ensemble de motifs).
Par exemple si j'indexe à partir de 0 et si j'ai lu successivement "bon", je sais que l'état courant du premier automate est 3 (je suis placé sur le n) et 0 pour le second (je suis placé sur ^). Si à l'itération suivante je lis le caractère 'z' par exemple, ceci interrompt la progression dans le premier automate et provoque un retour à l'état 0.
Plus généralement : je lis caractère par caractère le fichier de données
- Si le caractère me permet d'avancer dans l'automate, je le fais. Si j'atteins alors la fin de l'automate ($), alors le motif a été trouvé et je peux marquer ce motif comme trouvé. Je peux alors supprimer cet automate de mon ensemble d'automates à évaluer pour éviter d'avoir à faire des calculs par la suite dessus.
- Si le caractère ne me permet pas d'avancer dans un automate donné, alors je me replace sur l'état ^.
À la fin, je retourne tous les motifs qui n'ont pas été marqués (ils n'ont jamais été trouvés).
Plusieurs avantages à cette approche :
- on élimine les motifs traités (le pire cas étant comme d'habitude, les motifs que l'on cherche sont à la fin du fichier de données)
- on ne lit qu'une seule fois chaque ligne du fichier de données
- au lieu de marquer un motif (avec un boolean qui décrit s'il a été vu ou non), on peut maintenir un compteur ce qui permet de compter combien de fois il a été observer sans que cela impacte les performances de l'algorithme
- l'algorithme reste vrai pour des automates plus compliqués comme ceux engendrés par des expressions rationnelles
Troisième optimisation (sous certaines hypothèses et si tu sais programmer) :
Supposons maintenant qu'on se limite à la recherche de sous chaînes (donc on ne supporte pas les expressions rationnelles). Si les motifs à trouver sont "longs" et se ressemblent, on doit pouvoir encore un peu améliorer les performances en stockant les motifs de manières un peu plus subtile. Par exemple "bonjour" et "bonhomme". On peut alors stocker dans un arbre l'ensemble des noeuds.
^ \_b_o_n_j_o_u_r_$1 | \_h_o_m_m_e_$2 \_h_e_l_l_o_$3
L'idée consiste ensuite à marquer le(s) noeud(s) courant(s) de l'arbre et tronquer les branches qui ne servent plus quand on atteint un état final.
Bonne chance
dubcek
Messages postés
18752
Date d'inscription
lundi 15 janvier 2007
Statut
Contributeur
Dernière intervention
3 octobre 2024
5 619
Modifié par dubcek le 24/02/2012 à 09:06
Modifié par dubcek le 24/02/2012 à 09:06
certes, mais la boucle shell va exécuter ~40000 grep du fichier alors que le awk ne l'accède qu'une seule fois, le reste se passant en mémoire
morteldrv va nous indiquer les temps de calcul.
on peut rajouter un test qui ne recherche pas dans la ligne un motif déjà trouvé
morteldrv va nous indiquer les temps de calcul.
on peut rajouter un test qui ne recherche pas dans la ligne un motif déjà trouvé
awk 'NR==FNR {m[n++]=$0;next} {for(x=0;x<n;x++)if(!t[x])if($0 ~ m[x])t[x]++} END {for(x=0;x<n;x++)if(!t[x])print m[x]}'
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
mamiemando
Messages postés
33333
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
31 octobre 2024
7 800
24 févr. 2012 à 10:47
24 févr. 2012 à 10:47
Oui tout à fait d'accord, c'est d'ailleurs la première optimisation que je suggérais.
J'ai essayé de grappiller un peu en mettant les fichiers dans un ramdisk
mamiemando: ~11mn
dubcek : ta soluce a durée trop longtemps (>30mn) que j'ai laissé tombé.
j'ai bien essayé avec tes exemples ça marche.
le PC :
Intel(R) Pentium(R) 4 CPU 2.50GHz
RAM : 512Mo
mamiemando: ~11mn
dubcek : ta soluce a durée trop longtemps (>30mn) que j'ai laissé tombé.
j'ai bien essayé avec tes exemples ça marche.
le PC :
Intel(R) Pentium(R) 4 CPU 2.50GHz
RAM : 512Mo
dubcek
Messages postés
18752
Date d'inscription
lundi 15 janvier 2007
Statut
Contributeur
Dernière intervention
3 octobre 2024
5 619
24 févr. 2012 à 15:09
24 févr. 2012 à 15:09
et comme ça
$ readarray A < fichier_donnees ; for M in $(cat fichier_motif) ; do grep -q $M <<<"${A[@]}" || echo $M ; done
j'ai
-bash: readarray: command not found
PS :
@mamiemando
étant scripteur du dimanche, je ne vais donc pas trop entrer les optimisations que tu proposes ;)
-bash: readarray: command not found
PS :
@mamiemando
étant scripteur du dimanche, je ne vais donc pas trop entrer les optimisations que tu proposes ;)
dubcek
Messages postés
18752
Date d'inscription
lundi 15 janvier 2007
Statut
Contributeur
Dernière intervention
3 octobre 2024
5 619
Modifié par dubcek le 24/02/2012 à 15:43
Modifié par dubcek le 24/02/2012 à 15:43
avec quel awk as-tu testé, celui du post 3 ou du post 5 ?
quelle est ta version de bash ?
essayer
quelle est ta version de bash ?
$ bash --version GNU bash, version 4.1.5(1)-release (i486-pc-linux-gnu)
essayer
$ IFS=$'\n' ; A=($(< fichier_donnees)) ; for M in $(cat fichier_motif) ; do grep -q $M <<<${A[@]} || echo $M ; done
Modifié par mortelrdv le 23/02/2012 à 14:05
Oui, j'ai vraiment mal formulé.
Merci pour la soluce
Une question subsidiaire, y a t il un moyen d'accélérer la chose ?
car fichier_motif contient pas moins de 60000 lignes et y en a autant dans le fichier fichier_donnees
Cdlt