IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

regexp non greedy non fonctionnel


Sujet :

Langage Perl

  1. #1
    Membre éclairé
    Profil pro
    Assistant recherche bioinformatique
    Inscrit en
    Novembre 2007
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Assistant recherche bioinformatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 877
    Points : 835
    Points
    835
    Par défaut regexp non greedy non fonctionnel
    Bonjour,
    Je voudrais capturer la chaîne de caractère la plus petite d’après une expression régulière contenant une syntaxe non greedy, mais je capture toute la chaîne> Je voudrais uniquement cauptrer s:586:"Dear Dr. [node:author].
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    my $s = 's:7:"message";s:586:"Dear Dr. [node:author]';
    if ($s =~ /(s\:\d+\:.+?\:author\])/s ) { # author peut etre plusieurs lignes apres le s:\d+:, ces lignes peuvent aussi contenir des doubles quotes.
    	print "$1\n";
    }
    Je ne comprends pas mon erreur. La voyez vous ?
    Merci
    Z.

  2. #2
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    pour moi, ça marche exactement comme ça devrait marcher (et comme je m'attendais que ça marche) .

    Ou alors, si tu n'as pas le résultat escompté, alors précise:
    - le résultat que tu attends
    - ce que tu obtiens à la place.

  3. #3
    Membre éclairé
    Profil pro
    Assistant recherche bioinformatique
    Inscrit en
    Novembre 2007
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Assistant recherche bioinformatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 877
    Points : 835
    Points
    835
    Par défaut
    J'obtiens la ligne entière.

    Voici le code entier :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #!/usr/bin/perl
     
    use strict;
    use warnings;
    # use String::HexConvert ':all';
    # use PHP::Serialization qw(serialize unserialize);
    # use Data::Dumper;
     
    my $s = 's:7:"message";s:586:"Dear Dr. [node:author]';
    if ($s =~ /(s\:\d+\:.*?\:author\])/s ) {
    	print "$1\n";
     
    }
    __END__
    Et voici le terminal :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    perl remplace.pl 
    s:7:"message";s:586:"Dear Dr. [node:author]

  4. #4
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    Oui, c'est aussi ce que j'obtiens, et exactement ce à quoi je m'attendais en lisant ta regex. Le fait d'avoir "author" dans la regex l'oblige à aller jusque là. C'est quoi le résultat que tu recherches?

  5. #5
    Membre éclairé
    Profil pro
    Assistant recherche bioinformatique
    Inscrit en
    Novembre 2007
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Assistant recherche bioinformatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 877
    Points : 835
    Points
    835
    Par défaut
    Ce qui en gras ci-dessus : s:586:"Dear Dr. [node:author]

    Merci de ton aide.

  6. #6
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    OK, dans ce cas, le problème est double: d'une part il y a deux fois "s:", et la regex va commencer à matcher à partir de la première occurrence, et d'autre part la parenthèse ouvrante est mal placée (et tu captures plus que ce que tu veux). Voici un exemple de ce que tu veux faire sous le debugger, dans lequel j'ai ajouté le ";" en début de regex pour prendre le deuxième "s:":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
      DB<1>  $s = 's:7:"message";s:586:"Dear Dr. [node:author]';
     
      DB<2> print $1 if $s =~ /;s\:(\d+\:.*?\:author\])/s;
    586:"Dear Dr. [node:author]

  7. #7
    Membre éclairé
    Profil pro
    Assistant recherche bioinformatique
    Inscrit en
    Novembre 2007
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Assistant recherche bioinformatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 877
    Points : 835
    Points
    835
    Par défaut
    La chaine de caractere en gras comporte bien le s. De plus, c'est une version simplifier de la chaine a modifier qui est une chaine serializée d'un objet PHP, et qui comporte au moins 2 occurrences a trouver. Autrement dit voici les règles :
    1. trouver la chaine :author] (deux points + 'author' + crochet fermant)
    2. trouver en amont chercher la plus proche chaîne correspondant a s:\d+:" ('s' + deux points + suites de chiffres + deux points)
    3. retourner les chaines 1 et 2, ainsi que tous les caractères se trouvant entre les deux, ces caractères pouvant inclure des caractères d'espacement et des sauts de ligne.
    4. recommencer tant que le motif est trouvé


    Je ne peux pas copier la chaîne totale car il y a plein de caractères nuls qui polluent le copier coller. En tout cas, il y a plein de chose avant, et encore après. Je pensais que la recherche non greedy commençait a partir de la fin de la chaîne, et s’arrêtait des que le motif était trouver.

    je suis en train d’approcher le problème en PHP en simulant les classes sérialisées dans cette objet. Ainsi je compte obtenir une chaîne de caractère toute simple pour faire la recherche et le remplacement (str_replace).

    merci de ton temps.

  8. #8
    Membre éclairé
    Profil pro
    Assistant recherche bioinformatique
    Inscrit en
    Novembre 2007
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Assistant recherche bioinformatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 877
    Points : 835
    Points
    835
    Par défaut
    Citation Envoyé par Zwiter Voir le message
    La chaine de caractere en gras comporte bien le s. De plus, c'est une version simplifier de la chaine a modifier qui est une chaine serializée d'un objet PHP, et qui comporte au moins 2 occurrences a trouver. Autrement dit voici les règles :
    1. trouver la chaine :author] (deux points + 'author' + crochet fermant)
    2. trouver en amont chercher la plus proche chaîne correspondant a s:\d+:" ('s' + deux points + suites de chiffres + deux points)
    3. retourner les chaines 1 et 2, ainsi que tous les caractères se trouvant entre les deux, ces caractères pouvant inclure des caractères d'espacement et des sauts de ligne.
    4. recommencer tant que le motif est trouvé


    Je ne peux pas copier la chaîne totale car il y a plein de caractères nuls qui polluent le copier coller. En tout cas, il y a plein de chose avant, et encore après. Je pensais que la recherche non greedy commençait a partir de la fin de la chaîne, et s’arrêtait des que le motif était trouver.

    je suis en train d’approcher le problème en PHP en simulant les classes de cet objet. Ainsi je compte obtenir un objet deserialisé et je pourrai accéder a une chaîne de caractère toute simple pour faire la recherche et le remplacement (str_replace). Puis reserialisation.

    merci de ton temps.
    fausse manip pour editer mon message.

  9. #9
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    OK, DSL, j'ai lu trop rapidement. Suite de ma session sous le debugger pour le cas particulier (simplifié) que tu as exposé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      DB<3> print $1 if $s =~ /;(s\:\d+\:.*?\:author\])/s;
    s:586:"Dear Dr. [node:author]
    Sinon, la recherche non-gourmande (ou plutôt le quantificateur non gourmand) n'est pas ce que tu crois. La recherche se fait dans l'ordre normal, de gauche à droite, mais elle s'arrête dès qu'elle a trouvé une chaîne minimale la satisfaisant, alors qu'une recherche gourmande cherche la chaîne maximale.

    Pour tes règles plus compliquées, je regarderai plus tard ce soir, pas le temps maintenant (je vais dîner), mais ce serait bien que tu donnes des exemples pour mieux comprendre.

  10. #10
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    OK, j'ai regardé, avec ta description partant de la fin, ça paraît un peu compliqué, parce que les regex fonctionnent de gauche à droite et tu décris ton besoin dans le mauvais sens. A la limite, il faudrait inverser la chaîne, faire la recherche et inverser le résultat, mais ça ne paraît vraiment pas très propre (suite de la même session sous le debugger):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     DB<4> $t = reverse $s;
     
      DB<5> print $t;
    ]rohtua:edon[ .rD raeD":685:s;"egassem":7:s
      DB<6> $capture = $1 if $t =~ /(\]rohtua:.+?\d+\:s)/
     
      DB<7> p $capture
    ]rohtua:edon[ .rD raeD":685:s
       DB<8> $capture = reverse $capture
     
      DB<9> print $capture
    s:586:"Dear Dr. [node:author]
    OK, ça marche, car ici, le quantificateur non gourmand "+?" joue bien son rôle: s'arrêter au premier groupe de chiffres suivi de ":s" suivant ']rohtua:", mais c'est globalement d'une grande laideur.

    Il vaudrait mieux lire la chaîne dans l'ordre normal, mais bien définir tout ce que l'on est susceptible d'avoir entre le premier groupe, 's\:\d+\:' et le second groupe, '\:author\]', plutôt que d'utiliser une regex attrape-tout du genre '.*?'.

    Par exemple, si je suppose simplement que le caractère ':' est impossible entre celui suivant le groupe de chiffres et la section finale:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
      DB<1> $s = 's:7:"message";s:586:"Dear Dr. [node:author]';
     
      DB<2> print $1 if $s =~ /(s\:\d+\:[^:]+\:author\])/s;
    s:586:"Dear Dr. [node:author]
    ce qui se lit: 's:', suivi de chiffres, suivis de ':', suivis d'un certain nombre de caractères différents de ':', suivis de ':author]'.

    Cela marche avec ta chaîne, mais je ne peux pas savoir si cela correspond à ce que tu risques de rencontrer dans le cas général de tes données. Ici, je n'utilise plus de quantificateur non gourmand, parce que la classe inversée '[^:]+' s'avère plus efficace dans ce cas de figure.

  11. #11
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Comme il semble difficile de réaliser "toute l'opération d'extraction" avec une seule regexp, pourquoi ne pas récupérer la chaine extraite avec l'expression régulière initiale, et la spliter ensuite sur le caractère ";" en ne conservant que le dernier élément :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    my $s = 's:7:"message";s:586:"Dear Dr. [node:author]';
    if (my ($author) = $s =~ /(s\:\d+\:.+?\:author\])/s ) { # author peut etre plusieurs lignes apres le s:\d+:, ces lignes peuvent aussi contenir des doubles quotes.
      $author = (split ";", $author)[-1];
      print "$author\n";
    }
    qui donne :
    s:586:"Dear Dr. [node:author]

  12. #12
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Autre possibilité, si la chaine "non greedy" à capturer ne peut pas contenir de ";", remplacer le "." par la classe [^;]

    Mais j'imagine que la chaine en question peut contenir des ";", tu confirmes ? Si c'est le cas, il faudra aussi revoir le split de ma réponse précédente pour être plus "sélectif" (par exemple /;s=/, puis restituer "s=" dans le dernier élément extrait).

  13. #13
    Membre éclairé
    Profil pro
    Assistant recherche bioinformatique
    Inscrit en
    Novembre 2007
    Messages
    877
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Assistant recherche bioinformatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 877
    Points : 835
    Points
    835
    Par défaut
    Bonjour,
    Tout d'abord merci a vous 2 pour vos explications sur le fonctionnement des expressions régulières. J'ai résolu mon problème avec php, et en simulant les classes. Toutefois, je voulais trouver une solution en perl. Philou, j'ai pensé un peu prêt la même chose ce matin dans le bus : spliter ma grande chaîne par ';s:' (très très très peu probable), puis chercher toutes celles qui contiennent ':author]' et faire le remplacement. Et pour finir, je recolle le tout

    Lolo78, c'est quoi ton debugger ? ca fonctionne sous linux ? En tout cas, ca semble très pratique !

    Merci encore !
    Z.

  14. #14
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Le debugger est intégré à l'interpréteur perl : c'est l'option -d de lancement de perl :
    Sous le debugger, tu tapes la commande "h" pour avoir l'aide.
    Et voilà, à toi les points d'arrêts, la visualisation des variables et l'expérimentation d'expressions en dynamique.

  15. #15
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 256
    Points
    12 256
    Billets dans le blog
    1
    Par défaut
    Comme l'a dit Philou, le debugger est intégré à Perl sus toutes les plateformes. La syntaxe donnée par Philou permet de deboguer un programme appelé script.pl. Si tu ne veux pas déboguer un programme existant, mais tester des commandes Perl en direct comme je l'ai fait ci-dessus, alors il faut taper:
    Tu trouveras des explications complémentaires dans la section débogage de la faq Perl:

  16. #16
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 918
    Points : 6 743
    Points
    6 743
    Par défaut
    Le problème avec la sérialisation, c'est qu'il n'y a aucun moyen de savoir quand se termine une chaîne de caractère autrement qu'en se basant sur la longueur annoncée, vu que ni les guillemets doubles ni les points-virgules ne sont échappés.

    Voici une manière de contrôler que l'on est toujours dans la chaîne, et qui utilise (??{...}) pour construire dynamiquement la pattern en fonction de la longueur annoncée.

    version courte:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/usr/bin/perl
    use strict;
    use warnings;
     
    my $ser = 's:7:"message";s:586:"Dear Dr. [node:author]';
     
    if  ($ser =~ /(s:([0-9]+):"(??{"(?:[^:]|:(?!author])){0," . ($2-8) . "}"})(?::author]|.{0,10}(*SKIP)(*FAIL)))/s) {
    	print "$1\n";
    }
    version détaillée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #!/usr/bin/perl
    use strict;
    use warnings;
     
    my $ser = 's:7:"message";s:586:"Dear Dr. [node:author]';
     
    if  ($ser =~ /
    (
    	s:([0-9]+):"   #"#  on capture la longueur de la chaîne dans le groupe 2
     
    	# on injecte dynamiquement une sous pattern décrivant les caractères de la chaîne avant
    	# le motif. Chaque caractère est soit un caractère qui n'est pas ":", soit ":"
    	# non suivi par "author]"
    	#
    	# Pour être sûr de ne pas sortir de la chaîne (s:), on limite le nombre de caractères 
    	# directement dans le quantificateur {m,n} avant le motif recherché à :
    	#    capture2 - taille du motif  (":author]" fait 8 caractères)
     
    	(??{ 
    		"(?:[^:]|:(?!author])){0," . ($2-8) . "}"
    	})
     
    	(?:
    		:author] # on trouve le motif
    	  |
    		.{0,10}(*SKIP)(*FAIL) # dans le cas contraire, on matche jusqu'aux 10 caractères
                                  # suivants (soit les 8 caractères restant plus le double quote
                                  # et le point virgule), puis on force le moteur de regex à 
                                  # skipper tous les caractères précédents en faisant échouer la branche
                                  # avec (*FAIL). La prochaine tentative  se fera donc après le point-virgule
    	) 
    )/xs) {
    	print "$1\n";
    }
    NB: il est inutile d'ancrer la pattern avec (?:;|\A) dans la mesure où le caractère "s" ne peut se trouver que dans une chaîne ou pour annoncer une chaîne. Le contenu des chaînes étant skippé, l'ancrage n'est donc pas nécessaire.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Expression régulière "non greedy" en Java
    Par teletexte dans le forum Langage
    Réponses: 7
    Dernier message: 05/07/2014, 21h02
  2. clause de non sollicitation - non concurence
    Par Alwin dans le forum Contrat
    Réponses: 22
    Dernier message: 02/10/2009, 16h47
  3. Clavier du portable non reconnu, non démarrage de Vista
    Par Dennis Nedry dans le forum Périphériques
    Réponses: 12
    Dernier message: 12/07/2009, 12h41
  4. Lecteur CD/DVD non reconnu mais fonctionnel
    Par CmoiShinji dans le forum Composants
    Réponses: 1
    Dernier message: 20/08/2008, 12h58
  5. Réponses: 1
    Dernier message: 24/11/2006, 17h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo