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 :

Affiner une RegEx


Sujet :

Langage PHP

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    746
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 746
    Points : 316
    Points
    316
    Par défaut Affiner une RegEx
    Bonjour à tous,

    Voici une RegEx qui me permet d'ajouter des crochets autour des accords de guitare présents dans un texte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    '/(\b(?:G,C,D|A,B,C|E,C,D)|(?:[ABCDEFG](?:#|b)?)(?:\/[ABCDEFG]b)?(?:(?:(?:maj|min|sus|add|aug|dim)(?:\d{0,2}(?:#\d{1,2}|sus\d)?)?)|(?:m\d{0,2}(?:(?:maj|add|#)\d{0,2})?)|(?:-?\d{0,2}(?:\([^)]*\)|#\d{1,2})?))?)/'
    https://regex101.com/r/zREFE7/3

    Le problème que je rencontre est à la ligne "Comme un printemps..." où la lettre C de Comme est prise par la RegEx alors que je ne voudrais pas.

    Auriez-vous une idée pour affiner la RegEX et qu'elle ne prenne pas ce C ?

    Merci !

  2. #2
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 881
    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 881
    Points : 6 607
    Points
    6 607
    Par défaut
    De la même manière que tu as "balisé" le début d'une branche de ta pattern en utilisant un word-boundary \b, il te faut la même chose à la fin.

    Le problème c'est que pour le coup tu ne peux pas utiliser de word-boundary puisque certaines branches peuvent se terminer par une parenthèse fermante ou le dièse.

    La solution c'est de vérifier que la prochaine position est soit un caractère blanc (espace, tabulation, nouvelle ligne...), soit la fin de la chaîne. Pour ce faire on utilise un test avant (lookahead): (?=\s|\z) ou plus court en utilisant un test avant negatif: (?!\S) (position non suivie par un caractère qui n'est pas un caractère blanc.)




    Pour améliorer et voir les éventuelles erreurs dans une pattern aussi longue, il faut commencer par l'indenter. Pour ça on utilise le modificateur x afin que les espaces ne soient pas pris en compte et que l'on puisse ajouter des commentaires en ligne.

    Voici ta pattern indentée (j'ai changé le délimiteur / pour ~ ce qui évite d'avoir à échapper les / de la pattern):

    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    $pattern = <<<'EOD'
    ~
    ( # <-- ce groupe n'a pas besoin d'être capturant,
      # on peut trés bien mettre $0 dans le motif de remplacement.
      
        \b # <-- ce word-boundary devrait être en facteur avec toutes les
           # branches, pas juste cette branche en particulier.
           
        (?: # <-- Du coup, avec le point précédent, ce groupe devient inutile.
            G,C,D
          |
            A,B,C
          |
            E,C,D
        )
      |
        [ABCDEFG] # <-- [A-G]
        (?: \# | b )?
        (?: / [ABCDEFG] b )?
        (?:
            (?: # <-- groupe inutile
                (?: maj | min | sus | add | aug | dim )
                (?:
                    \d{0,2}
                    (?: \# \d{1,2} | sus \d )?
                )? # <-- ce groupe optionnel est inutile, car tout ce qu'il
                   # contient est déjà optionnel.
            )
          |
            (?: # <-- groupe inutile
                m \d{0,2}
                (?:
                    (?: maj | add | \# ) \d{0,2}
                )?
            )
          | # dernière branche
            (?: # <-- groupe inutile
                -? \d{0,2}
                (?:
                    \( [^)]* \)
                  |
                    \# \d{1,2}
                )?
            )
        )? # <-- comme une des branches (la dernière) contenue dans ce
           # groupe est constituée uniquement d'élément optionnel,
           # le quantificateur '?' est inutile. (a|b|c?)? <=> (a|b|c?)
    )
    ~x
    EOD;
    Ensuite on applique les corrections et on la remet un peu en forme pour ne pas qu'elle s'étende sur 2 km:
    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
    $pattern = <<<'EOD'
    ~
    \b
    (?:
        G,C,D | A,B,C | E,C,D
      |
        [A-G]   (?: \# | b )?   (?: / [A-G] b)?
        (?:
            (?: maj | min | sus | add | aug | dim )
            \d{0,2}
            (?: \# \d{1,2} | sus \d )?
          |
            m \d{0,2}
            (?: (?: maj | add | \# )?  \d{0,2} )?
          |
            -? \d{0,2} 
            (?: \( [^)]* \) | \# \d{1,2} )?
        )
    )
    (?! \S )
    ~x
    EOD;
     
    $result = preg_replace($pattern, '[$0]', $str);
    démo

    NB: depuis la version 7.4 de PHP, le test avant négatif (?! ... ) peut également s'écrire soit (*negative_lookahead: ... ), soit (*nla: ... )
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    746
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 746
    Points : 316
    Points
    316
    Par défaut
    Salut,

    Un grand merci pour ton coup de main de pro et l'explication claire qui va avec !

    Je rencontre encore une petite difficulté lorsque les accords sont séparés par un slash, exemple :

    https://3v4l.org/EgTM5

    Bon j'ai pu la contourner en faisant un explode php sur le / puis en appliquant ensuite ta regex, mais si cela n'est pas trop te demander je suis preneur de l'améliorer qui permettrait de prendre en compte le fait que le slash est un séparateur valide entre 2 accords.

    Merci encore !

  4. #4
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 881
    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 881
    Points : 6 607
    Points
    6 607
    Par défaut
    Je ne suis pas sûr qu'il s'agisse à proprement parler d'un séparateur (tel un espace). Le slash sert à préciser dans quel renversement se trouve l'accord, donc se serait plus [Gm/D] que [Gm]/[D]. Maintenant je ne suis pas expert de cet instrument et encore moins de cette notation d'accords, mon maximum à la guitare c'est le début de Jeux interdits (qui comme chacun ne le sait pas, n'est pas de Narciso Yepes mais d'un anonyme du XVIIe siècle) et Gare au gorille, donc à toi de creuser la question pour être sûre de ce que tu veux obtenir et voire, à la vue de tes découvertes, réécrire complètement la pattern.

    Si je suis motivé, je décortiquerai cette page. Tu peux en faire de même.

    Remarque: dans ce type de présentation (ligne d'accords suivie de la ligne avec les paroles correspondantes), les accords sont placés à une position de manière à ce que si on affiche le tout avec une police de caractère monospace et entre des balises <pre> pour que les espaces consécutifs soient pris en compte, l'accord se trouve à la position précise où il doit être joué par rapport aux paroles. Dans l'état actuel des choses, en ajoutant deux caractères pour chaque accord (les deux crochets), on décale tous les accords suivant de la même ligne de deux positions. Pour conserver, l'alignement (en supposant qu'il soit correct à l'origine), il faudrait finir la pattern par les éventuels deux espaces consécutifs. (pour retirer autant de caractères que l'on en ajoute).
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

Discussions similaires

  1. [RegEx] Aide pour une REGEX
    Par Death83 dans le forum Langage
    Réponses: 6
    Dernier message: 28/06/2006, 15h50
  2. Problème avec une RegEx
    Par Death83 dans le forum Général JavaScript
    Réponses: 12
    Dernier message: 26/05/2006, 14h03
  3. Insérer une variable dans une regex?
    Par Death83 dans le forum Général JavaScript
    Réponses: 6
    Dernier message: 21/05/2006, 11h28
  4. [RegEx] php et javascript dans une regex
    Par grochenel dans le forum Langage
    Réponses: 7
    Dernier message: 06/12/2005, 22h21
  5. [RegEx] spliter par rapport a une regex en récuperant la regex
    Par Khrysby dans le forum Langage
    Réponses: 1
    Dernier message: 10/11/2005, 15h08

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