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

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

Marsh Posté le 30-06-2009 à 20:34:51    

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)
Reply

Marsh Posté le 30-06-2009 à 20:34:51   

Reply

Marsh Posté le 01-07-2009 à 09:15:04    

: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é!  --
Reply

Marsh Posté le 01-07-2009 à 09:36:38    

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)
Reply

Marsh Posté le 01-07-2009 à 10:31:34    

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)
Reply

Marsh Posté le 04-07-2009 à 22:27:02    

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é!  --
Reply

Marsh Posté le 05-07-2009 à 02:30:38    

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)
Reply

Marsh Posté le 05-07-2009 à 04:09:53    

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é!  --
Reply

Marsh Posté le 05-07-2009 à 04:19:46    

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)
Reply

Marsh Posté le 09-07-2009 à 18:44:42    

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é!  --
Reply

Marsh Posté le 09-07-2009 à 19:03:05    

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)
Reply

Marsh Posté le 09-07-2009 à 19:03:05   

Reply

Marsh Posté le 10-07-2009 à 15:52:33    

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é!  --
Reply

Marsh Posté le 10-07-2009 à 18:35:47    

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)
Reply

Marsh Posté le 11-07-2009 à 18:28:58    

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é!  --
Reply

Marsh Posté le 11-07-2009 à 18:54:24    

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)
Reply

Marsh Posté le 12-07-2009 à 01:51:14    

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é!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed