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 PHP Discussion :

Capturer groupes répétés


Sujet :

Langage PHP

  1. #1
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 252
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 252
    Points : 8 540
    Points
    8 540
    Billets dans le blog
    17
    Par défaut Capturer groupes répétés
    Bonjour à tous,

    J'ai besoin de capturer (entre autres) une paire param/value présente 0 à n fois, or je ne récupère que la dernière occurrence.

    Je dois passer à côté de quelque chose, mais quoi ?

    Merci pour vos lumières


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php
     
    header('Content-Type: text/plain') ;
     
    $subject = 'name:content' ; // OK
    $subject = 'name;param1=value1:content' ; // OK
    $subject = 'name;param1=value1;param2=value2:content' ; // Manque param1/value1
     
    $regex = '/(\w+)(?:;(\w+)=(\w+))*:(\w+)/' ;
     
    preg_match_all($regex, $subject, $matches) ;
     
    print_r($matches) ;
    Donne :

    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
    Array
    (
        [0] => Array
            (
                [0] => name;param1=value1;param2=value2:content
            )
     
        [1] => Array
            (
                [0] => name
            )
     
        [2] => Array
            (
                [0] => param2
            )
     
        [3] => Array
            (
                [0] => value2
            )
     
        [4] => Array
            (
                [0] => content
            )
     
    )

  2. #2
    Membre émérite Avatar de tsuji
    Inscrit en
    Octobre 2011
    Messages
    1 558
    Détails du profil
    Informations forums :
    Inscription : Octobre 2011
    Messages : 1 558
    Points : 2 736
    Points
    2 736
    Par défaut
    L'approche la plus simple serait de focalisant les pairs paramètre/valeur directement, comme ça.
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $regex='/(?<=;)(\w+)=(\w+)(?=[:|;])/';
    preg_match_all($regex, $subject, $matches);
    print_r($matches);
    On se demande quel va mal dans le pattern original ? Le problème est que les capturés des sous-patterns avec 'wildcard' seraient remplacés l'un après l'autre laissant ce qu'est les derniers capturés.

    Si on fait explicitement deux fois le même pattern, il n'y a plus ce problème pour le dernier $subject:
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $count=2;
    $p=str_repeat('((?:;(\w+)=(\w+))', $count);
    $regex='/(\w+)'.$p.':(\w+)/';
    preg_match_all($regex, $subject, $matches);
    print_r($matches);
    On dirait ce n'est pas bien pratique et le nombre $count n'est pas dynamique et est trop restrictif. Est-ce possible de le faire en code? bien possible avec une détermination le $count préalable.
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $regex_count='/(?<=;)(\w+)=(\w+)(?=[:|;])/';
    preg_match_all($regex_count, $subject, $matches);
    $count=count($matches[0]);
    if ($count!=0) {
        $p=str_repeat('(?:;(\w+)=(\w+))', $count);
        $regex='/(\w+)'.$p.':(\w+)/';
        preg_match_all($regex, $subject, $matches);
        print_r($matches);
    } else {
        //faire quelque chose ou rien du tout
    }
    Mais là $regex_count est exactement ce que j'ai dans le début. Donc, si on fait ça pour la détermination de $count, on peut se demander pourquoi pas directement comme montré au début? Voilà mes opinions.

  3. #3
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 252
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 252
    Points : 8 540
    Points
    8 540
    Billets dans le blog
    17
    Par défaut
    Merci pour ton message

    Apparemment on ne peut récupérer que le dernier groupe capturé. J'ai trouvé ça pour un problème proche :

    The basic problem you are having [...] is a characteristic of all regex variants - a repeating capture group ONLY remembers that last matched text.
    http://regexadvice.com/forums/permal...ead.aspx#69729

    Je vais procéder en 2 étapes : extraction des 3 composantes name/params/content, puis traitement de params avec /;(\w+)=(\w+)/.

  4. #4
    Membre habitué Avatar de denissay
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2006
    Messages : 103
    Points : 125
    Points
    125
    Par défaut
    Juste pour information, tu peux en effet le faire en un seul regex et ce avec l'usage du \G qui, excusez la traduction, permet de commencer à partir de la positions de la fin du dernier 'match' ou du début de la chaine pour le premier 'match'...

    Donc, une solution possible utilisant un Lookahead positif est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?:'(\w+)|\G(?<!^))(?:;(\w+)=(\w+))?(?=.*?:(\w+)')
    DEMO

    Ou encore, sans Lookahead (à noter que "content" est optionnelle dans ce cas-ci):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?:'(\w+)|\G(?<!^))(?:;(\w+)=(\w+))?(?::([^']*)'.*$)?
    DEMO

    Plus d'informations sur le \G peuvent être trouvées ICI


  5. #5
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 252
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 252
    Points : 8 540
    Points
    8 540
    Billets dans le blog
    17
    Par défaut
    Woow je ne comprends pas bien le \G, doit me manquer les bases du fonctionnement des regexes.

    L'assertion \G est réalisée uniquement lorsque la position courante de l'occurrence trouvée est au début de l'occurrence, comme spécifié par l'argument offset de la fonction preg_match(). Elle diffère de \A lorsque la valeur du paramètre offset est différente de zéro.
    http://www.php.net/manual/fr/regexp....nce.escape.php

    Je garde ta proposition dans un coin et je verrai plus tard.

    Merci

Discussions similaires

  1. Groupe de capture pour expression reguliere
    Par cinou01 dans le forum Débuter
    Réponses: 4
    Dernier message: 05/02/2010, 14h17
  2. [RegEx] Groupes de captures devenus fous ?!!
    Par Sergejack dans le forum Langage
    Réponses: 1
    Dernier message: 17/11/2008, 13h44
  3. Expressions régulières et groupes capturants
    Par ®om dans le forum Langage
    Réponses: 1
    Dernier message: 09/01/2008, 15h37
  4. [RegEx] Capture de sous masques répétés à l'infini
    Par ametaireau dans le forum Langage
    Réponses: 2
    Dernier message: 31/07/2007, 17h42
  5. [regexp] comment capturer un groupe
    Par PhiLou421 dans le forum Collection et Stream
    Réponses: 1
    Dernier message: 21/06/2007, 01h01

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