Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1291 connectés 

  FORUM HardWare.fr
  Programmation
  Perl

  qu'utilisez-vous comme regexp pour matcher tout caractère (meme \n)

 


Qu'utiliseriez-vous ?




Attention si vous cliquez sur "voir les résultats" vous ne pourrez plus voter
Les invités peuvent voter

 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

qu'utilisez-vous comme regexp pour matcher tout caractère (meme \n)

n°1900752
MisterBark
be aware
Posté le 30-06-2009 à 20:34:51  profilanswer
 

Salut,
 
J'utilise à outrance les regexp en perl.
J'ai été récemment confronté à un grave probleme :
 
J'ai changé de serveur et ma version de perl est donc passée de 5.8.8 à 5.10.0.
J'ai découvert que lors de cette upgrade, perl avait changé la donne quant à l'interprétation des regexp:
les règles ne sont plus les mêmes !!!

Résultat: toutes les sources à modifier !
 
Voila un exemple qui illustre une différence nette (parmi combien ?)

Code :
  1. Un fichier test.txt contient des lignes de la forme :
  2. 2009-06-30_06:53:50 1246337630 0.0.0.0() test PROUT "bonjour tout le monde"
  3. 2009-06-30_06:53:51 1246337631 0.0.0.0() test PROUT "bonjour tout le monde"
  4. 2009-06-30_06:53:52 1246337632 0.0.0.0() test PROUT "bonjour tout le monde"
  5. J'ouvre ce fichier pour le mettre entièrement dans $temp.
  6. Puis :
  7. if( $temp =~ /^((.|\n)*)\s(\d{10})\s+\S+\s+\S+\s+PROUT\s/i ){
  8.   print("$3\n" );
  9. }


Dans l'ancienne version de perl (5.8.8), cela se comportait comme on peut l'imaginer:
Ca match tout en prenant le dernier PROUT s'il y en a plusieurs, en affichant donc "1246337632" dans mon exemple.
 
Mais dans le 5.10.0, ca ne matchera carrément plus ! le if est false !
 
Que l'on trouve cet exemple propre ou non, il y a tout de même un sacré problème !
Comment est-il possible que 2 versions de perl réagissent différemment sur un meme code ?
 
J'ai donc été amené à remettre mes sources en question et lorsque j'utilise la méthode suivante, ca match correctement :

Code :
  1. if( $temp =~ /^([\S\s]*)\s(\d{10})\s+\S+\s+\S+\s+PROUT\s/i ){
  2.   print("$2\n" );
  3. }


NB: j'ai juste remplacé (.|\n) par [\S\s].
cela supprime une parenthèse, sympa lorsque le motif est énorme avec des \10 et des $11 ... surtout lorsqu'il faut modifier toutes ses sources depuis des années ...
 
 
Je me pose donc encore la question suivante, qui est l'objet de ce topic :
Que faut-il utiliser lorsqu'on veut matcher tout caractère, y compris \n ?

 
Après réflexion je pense que mon (.|\n) était très sale et stupide car utiliser un | pour un seul caractère n'est pas franchement approprié (meme si ca devrait fonctionner)
J'ai donc pensé à [\S\s]
 
Quelqu'un a-t-il une meilleure idée ?
Qu'utilisez vous personnellement ?
 
MERCI ! :D


---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
mood
Publicité
Posté le 30-06-2009 à 20:34:51  profilanswer
 

n°1900869
gilou
Modérateur
Modzilla
Posté le 01-07-2009 à 09:15:04  profilanswer
 

:hello:  
 
Vérifies qu'il n'y a pas de problème avec la version de perl sur ce serveur, car elle a l'air buggée:
 
J'ai la version 5.10.0 (build 1004 du 3 septembre 2008 de Active State)
 
J'ai créé un fichier test.txt avec:
 

Code :
  1. 2009-06-30_06:53:50 1246337630 0.0.0.0() test PROUT "bonjour tout le monde"
  2. 2009-06-30_06:53:50 1246337631 0.0.0.0() test PROUT "bonjour tout le monde"
  3. 2009-06-30_06:53:50 1246337632 0.0.0.0() test PROUT "bonjour tout le monde"


 
j'ai écrit en vitesse ce test:
 

Code :
  1. #/usr/bin/perl
  2. use warnings;
  3. use strict;
  4.  
  5.  
  6. open my $FILE, '<', "test.txt";
  7. my $temp = join "", <$FILE>;
  8. close $FILE;
  9.  
  10. if( $temp =~ /^((.|\n)*)\s(\d{10})\s+\S+\s+\S+\s+PROUT\s/i ){
  11.   print("$3\n" );
  12. }


 
Et quand je lance le test, ça me donne bien "1246337632" comme résultat.
 
A+,


Message édité par gilou le 01-07-2009 à 09:19:28

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1900884
MisterBark
be aware
Posté le 01-07-2009 à 09:36:38  profilanswer
 

bon, alors en effet, je viens de faire le test et ca marche aussi chez moi.
Je n'aurais pas du simplifier mon exemple et prendre le cas réel !
 
Mais le cas réel est un fichier assez gros et un code assez gros aussi.
Je vais donc étudier ca pour comprendre à quel moment ca change entre mon cas réel et l'exemple.
Je reposte dès que j'ai un exemple simplifié qui fait le meme probleme. (c'est peut etre une question de taille de fichier... je vais voir ca)
 
Sinon, tu utiliserais quelle formule pour "tout caractère" ?
 
[\S\s] ca me parait pas mal mais ca reste encore sale je trouve à mon gout... mais j'ai pas vraiment d'autre idée...
 
merci !


---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
n°1900926
MisterBark
be aware
Posté le 01-07-2009 à 10:31:34  profilanswer
 

Ca y est, j'ai compris !!!
(pas l'explication du problème mais la différence entre mon cas réel et l'exemple)

 

-> c'est en effet une histoire de taille de fichier.

 

Voila :

Code :
  1. if( $temp =~ /^((.|\n)*)\S+\s(\d{10})\s+\S+\s+\S+\s+PROUT\s/i ){
  2. print("presize = ".length($1)."\n" );
  3. print("$3\n" );
  4. }


Si la taille de $1 est < 32768, ca marche.

 

Le problème est donc le suivant :
(.|\n)* match sans problème tout dans la version 5.8.8.
Mais dans la version 5.10.0, il ne pourra matcher que moins de 32Ko de données, après, ca ne matche plus !

 

Résultat: si on met 31.9Ko de données avant, ca renverra le premier PROUT (1246337630) puisqu'il ne peut pas inclure les 2 lignes PROUT dans (.|\n)* qui feraient alors plus de 32Ko.

 

C'est un peu comme si le * appliqué à une parenthèse commencait à chercher à 32Ko (ou 32K répétitions) et non au maximum de la chaine.
* appliqué à [\S\s] est en revanche illimité !

 

C'est incroyable, pourtant, c'était illimité dans le 5.8.8 !

 

Quelqu'un aurait une explication ?
et encore une fois: que faut-il utiliser ? [\S\s] ?

Message cité 1 fois
Message édité par MisterBark le 01-07-2009 à 10:57:02

---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
n°1902325
gilou
Modérateur
Modzilla
Posté le 04-07-2009 à 22:27:02  profilanswer
 

Tu as essayé ceci pour matcher le contenu de ton fichier:

Code :
  1. {                        # le slurp mode, vaut mieux pas le mettre en global
  2.  local $/;             # on se met en "slurp" mode
  3.  local $_ = <FH>;  # tu remplaces par un file handle sur ton fichier
  4.  if (/^((.|\n)*)\S+\s(\d{10})\s+\S+\s+\S+\s+PROUT\s/i ) {
  5.      print("presize = ".length($1)."\n" );
  6.      print("$3\n" );
  7.   }
  8. }


le slurp mode devrait coller, i.e. faire qu'on n'est pas limité en taille de l'input, sinon, c'est peut être un bug de cette version de Perl
 
Sinon, perso, je préfère (\n|.) ou plutôt (.|\n) a [\S\s] mais je pense que c'est du pareil au même en terme de performances.
 
A+,


Message édité par gilou le 04-07-2009 à 22:43:18

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1902344
MisterBark
be aware
Posté le 05-07-2009 à 02:30:38  profilanswer
 

Salut !
 
ok, en effet, mais le problème c'est que tu es obligé d'utiliser un fichier et non une variable.
or dans mon cas personnel c'est une variable, j'ai juste simplifié avec un fichier pour l'exemple !
 
Pour les performances je viens de faire le test ! :)
 
Voila :

Code :
  1. #!/usr/bin/perl
  2. use Time::HiRes qw(gettimeofday tv_interval);
  3. my $matcnt = 0;
  4. my $chrono = [gettimeofday];
  5. ##############################
  6. for( my $i=0 ; $i<10**6 ; $i++ ){
  7. my $temp = "ceci est du contenu bidon ! héhéhé
  8. ceci est du contenu bidon ! prout prout prout
  9. ceci est du contenu bidon ! héhéhé
  10. ceci est du contenu bidon ! prout prout prout
  11. ceci est du contenu bidon ! héhéhé
  12. ceci est du contenu bidon ! prout prout prout
  13. ceci est ma ligne a matcher :)
  14. ceci est du contenu bidon ! héhéhé
  15. ceci est du contenu bidon ! prout prout prout
  16. ceci est du contenu bidon ! héhéhé
  17. ceci est du contenu bidon ! prout prout prout
  18. ceci est du contenu bidon !
  19. ";
  20. if( $temp =~ /^[\S\s]*\smatcher\s/ ){ # CAS 1 : 10.498155 ns
  21. #if( $temp =~ /^([\S\s]*)\smatcher\s/ ){ # CAS 2 : 12.565238 ns
  22. #if( $temp =~ /^(.|\n)*\smatcher\s/ ){ # CAS 3 : 24.497492 ns
  23. #if( $temp =~ /^(\n|.)*\smatcher\s/ ){ # CAS 4 : 35.17497 ns
  24.  $matcnt++;
  25. }
  26. }
  27. ##############################
  28. $chrono = tv_interval($chrono, [gettimeofday]);
  29. print("matcnt = $matcnt\ntotal = $chrono secondes\ndonc boucle = $chrono ns\n" );
  30. exit;


 
Les résultats vont du simple au triple !
=> j'ai mis le temps que prend une boucle for à coté de chaque if.
(j'ai testé 3 fois pour chaque cas pour etre sur car c'était sur un pc déja assez occupé à faire autre chose)
 
CONCLUSIONS:
 
Donc le plus rapide est clairement [\S\s]
J'ai fait un cas 2 car je me doutais que le fait de mettre une parenthèse ralentirait un peu...
 
[\S\s] est 2 fois plus rapide que (.|\n)
[\S\s] est 3 fois plus rapide que (\n|.)
 
(.|\n) est plus rapide que (\n|.) sans doute parce qu'il y a plus souvent des . que des \n, or (\n|.) commence à chaque fois par chercher des \n.
 
PS: [\s\S] semble strictement aussi rapide que [\S\s]


---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
n°1902346
gilou
Modérateur
Modzilla
Posté le 05-07-2009 à 04:09:53  profilanswer
 

MisterBark a écrit :

Ca y est, j'ai compris !!!
(pas l'explication du problème mais la différence entre mon cas réel et l'exemple)
 
-> c'est en effet une histoire de taille de fichier.
 
Voila :

Code :
  1. if( $temp =~ /^((.|\n)*)\S+\s(\d{10})\s+\S+\s+\S+\s+PROUT\s/i ){
  2. print("presize = ".length($1)."\n" );
  3. print("$3\n" );
  4. }


Si la taille de $1 est < 32768, ca marche.
 
Le problème est donc le suivant :
(.|\n)* match sans problème tout dans la version 5.8.8.
Mais dans la version 5.10.0, il ne pourra matcher que moins de 32Ko de données, après, ca ne matche plus !

 
Résultat: si on met 31.9Ko de données avant, ca renverra le premier PROUT (1246337630) puisqu'il ne peut pas inclure les 2 lignes PROUT dans (.|\n)* qui feraient alors plus de 32Ko.
 
C'est un peu comme si le * appliqué à une parenthèse commencait à chercher à 32Ko (ou 32K répétitions) et non au maximum de la chaine.
* appliqué à [\S\s] est en revanche illimité !
 
C'est incroyable, pourtant, c'était illimité dans le 5.8.8 !
 
Quelqu'un aurait une explication ?
et encore une fois: que faut-il utiliser ? [\S\s] ?


Ce n'est pas le comportement que je vois: Il passe dans le test, même si on depasse 32ko
Il matche (j'ai testé avec une variable et un fichier, tous deux de taille 76ko), mais il discarde de $1 a partir du 32700e caractere (inclus)
La taille max de ce qui est stocké dans $1 est 32699 apparement et quand il atteint cette taille max, le matching complet avec l'assignation de $3 est effectuée  (ce qu'on a dans $3 n'est pas la dernière valeur du fichier si celui ci fait plus de 32ko.)
 
C'est un bug de cette version de perl, signalé dans la base des bugs de perl
 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1902348
MisterBark
be aware
Posté le 05-07-2009 à 04:19:46  profilanswer
 

oui bien sur ca revient à ce que je disais :
 
lorsqu'on utilise * (et non *? ) ca commence à matcher au maximum.
et donc (.|\n)* devrait commencer à faire le maximum possible, c'est à dire tout le fichier (toute la variable)
or dans 5.10.0, ca commence à matcher à 32Ko de données et non après.
 
Lorsque je disais "ca ne matche pas" c'est parce que mon PROUT était plus loin que 32768 caractères.
 
Sinon je crois que la réponse à mon topic est donc définitivement [\S\s] car bien plus rapide ! (et au moins ca marche dans tous les cas)

Message cité 1 fois
Message édité par MisterBark le 05-07-2009 à 04:21:13

---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
n°1904181
gilou
Modérateur
Modzilla
Posté le 09-07-2009 à 18:44:42  profilanswer
 

MisterBark a écrit :

or dans 5.10.0, ca commence à matcher à 32Ko de données et non après

Ça arrête de matcher a 32Ko de données, en fait.
A+,  


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1904190
MisterBark
be aware
Posté le 09-07-2009 à 19:03:05  profilanswer
 

non, ca arrêterait de matcher à 32K si on faisait comme ceci :
/^((.|\n)*?)PROUT/
 
or en l'absence de "?", ca commence à matcher au maximum, donc en l'occurrence 32K :
/^((.|\n)*)PROUT/
 
Dans le premier cas, ca essayera de matcher de 0 à 32K de données alors que dans le second c'est l'inverse: de 32K à 0.


---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
mood
Publicité
Posté le 09-07-2009 à 19:03:05  profilanswer
 

n°1904452
gilou
Modérateur
Modzilla
Posté le 10-07-2009 à 15:52:33  profilanswer
 

MisterBark a écrit :

non, ca arrêterait de matcher à 32K si on faisait comme ceci :
/^((.|\n)*?)PROUT/
 
or en l'absence de "?", ca commence à matcher au maximum, donc en l'occurrence 32K :
/^((.|\n)*)PROUT/
 
Dans le premier cas, ca essayera de matcher de 0 à 32K de données alors que dans le second c'est l'inverse: de 32K à 0.

:non: j'ai testé avec des tailles de fichier variables
Ca matche jusqu'à 32k, et ca n'essaye pas de matcher au dela (ie la valeur que tu as en $3 est celle associée a la taille maximale de $1, ie 32k). Tout se passe comme si, lorsque $1 avait atteint la taille maximale admissible pour lui (32k ici), il arrêtait de matcher.
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1904488
MisterBark
be aware
Posté le 10-07-2009 à 18:35:47  profilanswer
 

Salut,
 
ok, donc je crois que tu n'as pas compris comment fonctionne "*"
 
* répèete au maximum ce qui précède (caractère unique, parenthèse entière ou jeu de caractères [...])
 
MAIS,
* seul commence  à matcher au MAXIMUM possible.
 
Par ex:

Code :
  1. $var = "abc PROUT1 def PROUT2 xyz"
  2. if( $var =~ /^.* PROUT(\d)/ ){
  3.   print("$1\n" );
  4. }


-> ici, c'est "2" qui s'affichera, et non "1".
Ceci parce que .* signifie "autant de caractères que possible (sauf \n)
.* COMMENCE donc à matcher au maximum possible: toute la chaine.
 
Maintenant :

Code :
  1. $var = "abc PROUT1 def PROUT2 xyz"
  2. if( $var =~ /^.*? PROUT(\d)/ ){
  3.   print("$1\n" );
  4. }


-> ici, c'est "1" qui s'affichera, et non "2".
Parce que *? signifie "tout sauf \n autant de fois que possible", MAIS "en commencant par le plus petit possible".
 
Donc,
lorsqu'on utilise (.|\n)* et qu'on s'apercoit qu'il ne peut pas matcher plus de 32Ko de données, c'est non pas parce qu'il s'arrête de matcher en arrivant à 32Ko, mais parce qu'il COMMENCE à 32K avant de descendre à 0.
(.|\n)*? en revanche, commence à 0 et s'arrête à 32Ko.
 
Voila, juste une petite nuance sur le fonctionnement des regexp :)
@++


---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
n°1904626
gilou
Modérateur
Modzilla
Posté le 11-07-2009 à 18:28:58  profilanswer
 

La tu fais des suppositions sur la manière dont le moteur d'expression régulière fonctionne.
Sans doc d'implémentation, je n'ai aucune raison de croire que ça fonctionne de cette manière (ie de la droite vers la gauche, par réduction, et non de la gauche vers la droite, par augmentation, surtout que cette dernière méthode est celle habituellement utilisée par les automates d'états finis associés aux expressions régulières).
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
n°1904627
MisterBark
be aware
Posté le 11-07-2009 à 18:54:24  profilanswer
 

comment peut-on imaginer que lorsqu'on fait (.|\n)* ca commencerait à chercher au plus petit, pour laisser passer des "solutions" qui matchent, puis une fois arrivé à la fin, revenir en arrière en disant "ah oui, il y avait ca qui matche donc c'est ca le plus grand...

 

Ce serait terriblement mal concu !

 

Il est évident que lorsqu'on utilise *, ca commence directement a chercher  au plus grand, et tu t'en apercevrais forcément en faisant des tests de rapidité :

 

/^(.|\n)*PROUT/

 

Test 1:
variable de 31K de données puis PROUT après.

 

Test 2:
variable de 31K de données mais avec PROUT qu'au tout début.

 

En utilisant ma boucle de chrono, tu verrais que le test 1 est plus rapide.

 

Inversement, si on utilisait (.|\n)*? c'est le 2 qui serait plus rapide.

Message cité 1 fois
Message édité par MisterBark le 11-07-2009 à 18:55:12

---------------
La vie c'est comme une boite de chocolats, on ne sait jamais sur quoi on va tomber. (Forrest Gump)
n°1904657
gilou
Modérateur
Modzilla
Posté le 12-07-2009 à 01:51:14  profilanswer
 

MisterBark a écrit :

comment peut-on imaginer que lorsqu'on fait (.|\n)* ca commencerait à chercher au plus petit, pour laisser passer des "solutions" qui matchent, puis une fois arrivé à la fin, revenir en arrière en disant "ah oui, il y avait ca qui matche donc c'est ca le plus grand...
 
Ce serait terriblement mal concu !
 

Tout simplement parce que c'est comme ça que ça marche si vous remplacez votre * par un {n} (ou n est en entier positif). Et parce que si on connait un peu la théorie sous-jacente au pattern matching, c'est a dire les automates d'état fini, on sait que c'est ainsi que c'est en général implémenté.
Et non, on ne revient pas en arrière une fois arrivé a la fin: chaque fois qu'on trouve une nouvelle solution, elle remplace la solution courante, s'il y en a une. Ainsi, quand on arrive a la fin, on prend la solution courante (si elle existe).
Mais bon, comme j'ai dit, je ne sais pas si dans le cas d'une *, c'est l'algo pour {n} qui est appliqué, ou si il y a une optimisation particulière, faisant commencer par la fin. Je n'ai pas eu l'occasion de lire le source C du moteur d'expression régulière de Perl.
A+,


Message édité par gilou le 12-07-2009 à 02:16:08

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Perl

  qu'utilisez-vous comme regexp pour matcher tout caractère (meme \n)

 

Sujets relatifs
Petit problème de structure / chaine de caractèreVous utilisez SNMP ?
Caractère séparateur sous excel VBA[Resolu]Tronquer une chaine de caractère
[ksh] Récupérer un morceau d'une chaine de caractèreComparaison de String et caractère universel
Erreur Nonetype sur une regexp pourtant reconnue [résolu][Shell] Supprimer les répétitions de caractère
[Mysql] Ajout d'un caractere "0" sur toutes les valeurs d'un champspreg_match replace et REGEXP
Plus de sujets relatifs à : qu'utilisez-vous comme regexp pour matcher tout caractère (meme \n)


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR