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 :

Exclusion d'une chaine dans une expression [RegEx]


Sujet :

Langage PHP

  1. #1
    Membre confirmé

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    650
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 650
    Points : 546
    Points
    546
    Par défaut Exclusion d'une chaine dans une expression
    Bonjour,

    j'ai le script suivant :
    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
    <?
    $content = '
    AA
    {#IF MENU=1}
      BB
      {#IF TEST=1}
        CC
      {#ENDIF}
      EE
    {#ELSE}
          FF
    {#ENDIF}';
     
      if ( preg_match('`\{#IF([[:space:]]+)([a-z0-9]*)([[:space:]]*)([<>=])([[:space:]]*)([a-z0-9]*)\}(.*){#ENDIF}`isU', $content, $matches/*, PREG_OFFSET_CAPTURE*/) ) {
        echo '<pre>';
        echo htmlentities(print_r($matches, true));
        die;
      }
      echo 'not found';
      die;
    ?>
    Mon but est d'obtenir le block if / endif central (celui contenant "CC"), je ne vois pas quoi mettre dans mon expression à la place du (.*) pour l'exclure.

    Malgres le "U", j'obtiens le bloc complet.

    Merci de votre aide

  2. #2
    Membre éclairé
    Avatar de hornetbzz
    Homme Profil pro
    Directeur commercial
    Inscrit en
    Octobre 2009
    Messages
    482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 56
    Localisation : France

    Informations professionnelles :
    Activité : Directeur commercial

    Informations forums :
    Inscription : Octobre 2009
    Messages : 482
    Points : 773
    Points
    773
    Par défaut
    Salut,


    je ne suis pas le champion du monde des regex, donc c'est juste une suggestion :

    Pourquoi n'essayes tu pas plutôt de :
    - récupérer le texte qui est juste avant les ENDIF,
    - Quand ça matche, tu mets le résultat dans un tableau (grace aux parenthèses capturantes).
    - et après le preg_match (d'ailleurs il faudrait plutôt utiliser preg_match_all dans ce cas là), tu sors le dernier résultat trouvé, comme ça tu auras le texte de ton ENDIF le plus profond.

    PS: et tu as doit avoir un problème de copier/coller car dans mon éditeur, le commentaire est collé à $matches.

  3. #3
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Salut,



    J’espère qu’il n’y a pas d’erreur de recopiage parce que l’équivalent de ceci en Python marche:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    '`\{#IF[[:space:]]+[a-z0-9]+[[:space:]]*[<>=][[:space:]]*[a-z0-9]+}\n[[:space:]]+((?:(?<!\{#IF).)*)\n[[:space:]]*\{#ENDIF}`isU'




    Il ne faut pas oublier la présence de ’\n’ d’une ligne à l’autre.
    J’ai mis ’\n’ mais dans ton cas c’est peut être ’\r’ ou ’\r\n’.



    Il ne sert à rien de mettre [[:space:]]+ et [<>=] entre parenthèses, et sans doute pas plus à en mettre autour de [a-z0-9]*
    Plus il y a de groupes capturant dans une RE, plus elle est ralentie par la nécessité d’enregistrer les matching/non matching des groupes.



    J’ai mis deux fois [[:space:]]+ au lieu de [[:space:]]*
    - une fois après #IF
    - l’autre fois après la ligne {#IF TEST=1}
    Sinon ce sont de drôles d’instructions IF



    Par contre il faut absolument [[:space:]]*\{#ENDIF} avec une étoile * car le bloc IF peut être collé contre la gauche du script.



    Quand il n’y a pas d’accolade gauche { non échappée en amont d’une accolade droite } , cette dernière n’a pas besoin d’être échappée.
    Idem s’il y a une accolade \{ gauche échappée en amont.



    Enfin ça ne marcherait pas si le point ne matchait pas les fins de ligne et si
    n’avait pas son appétit réduit.
    Mais c’est bon, il y a les options s et U dans ta RE.

  4. #4
    Membre confirmé

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    650
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 650
    Points : 546
    Points
    546
    Par défaut
    Citation Envoyé par hornetbzz Voir le message
    Pourquoi n'essayes tu pas plutôt de :
    - récupérer le texte qui est juste avant les ENDIF,
    - Quand ça matche, tu mets le résultat dans un tableau (grace aux parenthèses capturantes).
    - et après le preg_match (d'ailleurs il faudrait plutôt utiliser preg_match_all dans ce cas là), tu sors le dernier résultat trouvé, comme ça tu auras le texte de ton ENDIF le plus profond.

    PS: et tu as doit avoir un problème de copier/coller car dans mon éditeur, le commentaire est collé à $matches.
    J'y ai pensé mais je voulais éviter ce bricolage :/ pour le commentaire, non, c'est bien cas mais ca ne pose pas de soucis chez moi


    Citation Envoyé par eyquem Voir le message
    Il ne faut pas oublier la présence de ’\n’ d’une ligne à l’autre.
    J’ai mis ’\n’ mais dans ton cas c’est peut être ’\r’ ou ’\r\n’.



    Il ne sert à rien de mettre [[:space:]]+ et [<>=] entre parenthèses, et sans doute pas plus à en mettre autour de [a-z0-9]*
    Plus il y a de groupes capturant dans une RE, plus elle est ralentie par la nécessité d’enregistrer les matching/non matching des groupes.



    J’ai mis deux fois [[:space:]]+ au lieu de [[:space:]]*
    - une fois après #IF
    - l’autre fois après la ligne {#IF TEST=1}
    Sinon ce sont de drôles d’instructions IF



    Par contre il faut absolument [[:space:]]*\{#ENDIF} avec une étoile * car le bloc IF peut être collé contre la gauche du script.



    Quand il n’y a pas d’accolade gauche { non échappée en amont d’une accolade droite } , cette dernière n’a pas besoin d’être échappée.
    Idem s’il y a une accolade \{ gauche échappée en amont.
    Merci pour ces précisions, par contre, pour les \n, pas besoin visiblement coté php, car c'est pris en compte par l'option multi-lignes "s"

    Une fois les \n supprimés (j'ai d'abord testé en remplaçant les simple quote (') par des doubles ("), ca a rien changé), hop... ca marche bien joué

    par contre, je serais preneur d'une explication sur Merci beaucoup pour votre aide !

  5. #5
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    En Python, quand on écrit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    content = """
    AA
    {#IF MENU=1}
      BB
      {#IF TEST=1}
        CC
      {#ENDIF}
      EE
    {#ELSE}
          FF
    {#ENDIF}"""
    content prend en réalité la valeur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "\nAA\n{#IF MENU=1}  BB\n{#IF TEST=1}\n    CC\n  {#ENDIF}\n  EE\n{#ELSE}      FF\n{#ENDIF}"
    Je ne connais pas bien PHP mais j’ai pris la liberté de penser que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $content = '
    AA
    {#IF MENU=1}
      BB
      {#IF TEST=1}
        CC
      {#ENDIF}
      EE
    {#ELSE}
          FF
    {#ENDIF}';
    donne à $content une valeur semblable, avec des \n.




    Or si on veut attraper CC, et uniquement CC, présent dans
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    {#IF TEST=1}\n    CC\n  {#ENDIF}
    sans les blancs et les \n qui entourent CC,
    il faut, je n’en suis pas moins convaincu que précédemment, que la RE comporte \n[[:space:]]+ devant et \n[[:space:]]+ derrière.




    Il ne faut pas faire une confusion.

    L’option s permet au point qui se trouve dans de matcher avec \n ==> s’il y a des \n dans CC , la partie X va pouvoir traverser CC sans être arrêtée.

    Ça ne veut pas dire que la RE ne doive alors pas du tout comporter de fin de ligne. Placer des \n explicites dans la RE a strictement le même effet que placer tout autre caractère plus habituel: cela constitue un caractère fixe supplémentaire parmi ceux auxquels la RE peut se cramponner. Je veux dire que pour attraper avec une RE une partie variable dans une chaîne, il faut bien qu’il y ait suffisamment de points fixes dans la RE pour délimiter cette variabilité.

    Donc je persiste à penser qu’il faut ces \n dans la RE Mais si tu as dû les enlever, c’est que ça ne marchait pas avec. C’est embêtant. Je ne comprends pas. Quel était l’effet ? Et maintenant que tu as enlevé les \n, qu’est ce que tu obtiens comme résultat ? Il est OK ?
    Ce serait bien d’arriver à éclaircir pour savoir à quoi s’en tenir.



    --------------------------------------------------


    c’est le groupe capturant (.*) avec une condition (?<!\{#IF)


    (.*) court de ’{#IF TEST=1}\n\s+’ à la première sous-chaîne ’\n\s*{#ENDIF}’ rencontrée (\s signifie blanc en Python)

    Mais pour que ’{#IF TEST=1}\n\s+’ soit la plus proche de ’\n\s*{#ENDIF}’ , il faut s’assurer que le point court à travers des caractères dans lesquels il ne rencontre pas le motif ’{#IF’

    Il y a peut être un autre moyen de faire, mais je n’en ai trouvé qu’un seul pour vérifier ça: faire vérifier au moteur de regex que, tant qu’il n’a pas atteint son but ( le motif ’\n\s*{#ENDIF}’ ), chaque fois qu’il avance le point d’un caractère dans la chaîne explorée, il ne vient pas de dépasser le motif ’{#IF’, c’est à dire que le nouveau caractère n’est pas précédé par ce motif.



    (?<!\{#IF) est une assertion négative vers l’arrière
    Une assertion est toujours (? ........ ) ,
    négative vers l’avant (?! ........ ), positive vers l’avant (?= ........ )
    négative vers l’arrière (?<! ........ ), positive vers l’arrière (?<= ........ )

    (?<!\{#IF) est vérifiée si la position à laquelle elle est examinée n’est pas précédée de 4 caractères {#IF.

    Etant donné que j’ai écrit (?<!\{#IF). cela signifie que l’assertion est examinée en remontant à partir de la gauche du nouveau caractère examiné par le moteur de regex.

    Mais on pourrait écrire aussi .(?<!\{#IF)

    Dans ce cas l’assertion serait examinée à partir de la droite du nouveau caractère, c’est à dire que le moteur de regex regarderait si le caractère nouveau est ’F“ et les 3 caractères en amont sont ’{#I’





    J’ai estimé qu’il était préférable de mettre une assertion en arrière qu’en avant. Mais je ne sais plus pourquoi !
    Avec ((?:.(?!\{#IF))*?) , cela marche aussi en effet.

    (?:.......) définit un groupe capturant.
    J’estime que quand on n’a pas besoin de la sous-chaîne matchante capturée par un groupe, il est en effet inutile et encombrant de laisser capturant le groupe et qu’il est mieux de le rendre non capturant.




    Les parenthèses en rouge sont obligatoires; sans elles ça ne marche pas. C’est une conclusion après essai de ma part, obtenue par tatônnement, pas une règle théorique.
    Par conséquent il faut les rendre non capturantes avec ?:

  6. #6
    Membre confirmé

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    650
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 650
    Points : 546
    Points
    546
    Par défaut
    Donc je persiste à penser qu’il faut ces \n dans la RE Mais si tu as dû les enlever, c’est que ça ne marchait pas avec. C’est embêtant. Je ne comprends pas. Quel était l’effet ? Et maintenant que tu as enlevé les \n, qu’est ce que tu obtiens comme résultat ? Il est OK ?
    Ce serait bien d’arriver à éclaircir pour savoir à quoi s’en tenir.
    Ben, l'effet est que cela fonctionne

    Merci pour ces explications

  7. #7
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Dommage, je ne comprends pas.

    Qu’est ce qui fonctionne ?
    La RE avec les ’\n’ que j’ai mis dans ma RE,
    ou sans ’\n’ , ce que tu dis avoir été obligé de faire ?

    Ce qui serait bien, c’est que tu montres le texte exploré et le résultat que tu obtiens avec et sans les ’\n’ dans la RE.

  8. #8
    Membre confirmé

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    650
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 650
    Points : 546
    Points
    546
    Par défaut
    désolé,

    j'avais raté ta réponse et comme ca fonctionne bien, j'étais passé à autre chose

    voici mon appel :

    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
    <?
    $content = '
    AA
    {#IF MENU=1}
      BB
      {#IF TEST=1}
        CC
      {#ENDIF}
      EE
    {#ELSE}
          FF
    {#ENDIF}';
     
      if ( preg_match("`\{#IF[[:space:]]+([a-z0-9]+)[[:space:]]*([<>=!]+)[[:space:]]*([a-z0-9]+)}((?:(?<!\{#IF).)*)\{#ENDIF}`isU", $content, $matches) ) {
        echo '<pre>';
        echo htmlentities(print_r($matches, true));
        die;
      }
      echo 'not found';
      die;
    ?>
    et voici ce que j'obtiens :

    Array
    (
    [0] => {#IF TEST=1}
    CC
    {#ENDIF}
    [1] => TEST
    [2] => =
    [3] => 1
    [4] =>
    CC

    )
    C'est donc bien le noeud central

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

Discussions similaires

  1. recherche d'une chaine dans une chaine
    Par Katachana dans le forum Langage
    Réponses: 2
    Dernier message: 15/07/2008, 12h10
  2. recherche d'une chaine dans une chaine
    Par jpclutier dans le forum Shell et commandes GNU
    Réponses: 3
    Dernier message: 03/12/2007, 11h30
  3. Tester une chaine dans une chaine
    Par ulysse031 dans le forum Algorithmes et structures de données
    Réponses: 13
    Dernier message: 26/03/2007, 00h48
  4. chercher caractére d'une chaine dans une chaine
    Par ulysse031 dans le forum Langage
    Réponses: 25
    Dernier message: 21/03/2007, 18h09
  5. Réponses: 2
    Dernier message: 19/10/2005, 15h38

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