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 :

Pattern PCRE non exclusif [RegEx]


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné Avatar de T`lash
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Saint-Pierre-Et-Miq.

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 381
    Par défaut Pattern PCRE non exclusif
    Bonjour,

    Afin de développer un module de routage pour une appli web, je dois faire un usage intensif des expressions rationnelles.
    Je dois notamment tester des URI de la forme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    /Services/NOM_DU_SERVICE/Liste/de/Parametres/ou/pas
    Le nom du service est obligatoire, mais pas les paramètres en fin de ligne.
    La ligne peut indifféremment se terminer par un slash ou pas.
    Les paramètres ne peuvent contenir d'autres caractères spéciaux que '-' et '_'.

    Le problème que j'ai actuellement c'est qu'on peut rajouter n'importe quel caractère spécial à la fin de la chaine sans en empêcher la validation.
    Toutes les solutions que j'ai pu tester jusque là ont fait que la capture de certains paramètres ne soient plus assurée.

    Voici l'expression en question :

    https://regex101.com/r/lB7nL8/1

    Vous voyez que si on ajoute "+" à la fin de la chaine elle est toujours valide alors que ça ne devrait pas être le cas.


    Merci pour votre aide.

  2. #2
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 020
    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 : 3 020
    Par défaut
    Le problème c'est qu'à aucun moment tu n'utilises l'ancre de fin de chaîne \z pour vérifier que les caractères utilisés sont autorisés. Voici une version commentée en free-space mode (modificateur x). Note que j'ai changé le délimiteur, donc les slashes ne sont plus échappés:
    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
    ~
    (?:
        # la branche contigue a pour vocation d'être
        # employée éventuellement plusieurs fois, il
        # faut donc la placer en premier
     
        \G(?!\A) / # on place le slash ici pour
                   # pour accélérer l'échec
      |
        # on vérifie dés la branche d'entrée que le format
        # est conforme
     
        /Services/
        # on utilise des quantificateurs possessifs pour
        # échouer plus rapidement
        (?'var'[[:alnum:]_-]++)   
        # on vérifie qu'il n'y a que des caractères autorisés
        # jusqu'à la fin avec un lookahead
        (?= [[:alnum:]/_-]*+ \z)
        /? # on place un slash optionel
    )
    # le groupe non capturant n'est plus utile, on l'enlève
    (?'params'[[:alnum:]_-]+)?
    ~x
    lien regex101

    NB: si la chaîne à traiter ne contient que le path de ton exemple, ajoute l'ancre de début de chaîne \A à la deuxième branche.

    On peut aussi changer la pattern de cette manière (les paramètres ne sont alors extrait qu'à partir du deuxième tour):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    ~
        \G(?!\A) / (?'params'[[:alnum:]_-]+)
      |
        \A /Services/
        (?'var'[[:alnum:]_-]++)   
        (?= [[:alnum:]/_-]*+ \z)
    ~x
    Concernant le lookahead pour ces deux patterns, rien n'interdit de le rendre plus explicite pour interdire deux slashs consécutifs:Après tu peux employer une solution plus rustique (mais peut être un peu plus lente) en contrôlant le format avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ~\A/Services/(?<var>[\w-]+)(?<params>(?:/[\w-]+)*+/?)\z~
    Puis en séparant les paramètres dans un deuxième temps.

  3. #3
    Membre chevronné Avatar de T`lash
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Saint-Pierre-Et-Miq.

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 381
    Par défaut
    On m'avait proposé cette solution sur StackOverflow, mais la tienne est nettement plus optimisée.

    Merci beaucoup pour ton aide.

  4. #4
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 020
    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 : 3 020
    Par défaut
    Cette solution est fausse car il suffit de rajouter une lettre après un caractère non-autorisé pour que ça passe (le lookahead ne testant que le dernier caractère). En plus vu que le lookahead est mis en facteur au début de la pattern, il est testé à chaque fois pour rien.

  5. #5
    Membre chevronné Avatar de T`lash
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Saint-Pierre-Et-Miq.

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 381
    Par défaut
    Citation Envoyé par CosmoKnacki Voir le message
    Cette solution est fausse car il suffit de rajouter une lettre après un caractère non-autorisé pour que ça passe (le lookahead ne testant que le dernier caractère). En plus vu que le lookahead est mis en facteur au début de la pattern, il est testé à chaque fois pour rien.
    Ce qui explique donc pourquoi il y a beaucoup plus d'opérations.

    Le soucis avec les regex, c'est que souvent on pense que ça passe, mais c'est simplement parce qu'on n'a pas testé toutes les situations possibles. Il y en a toujours une pour faire foirer ce que l'on croyait fonctionnel.

  6. #6
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 020
    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 : 3 020
    Par défaut
    Oui entre autre, ainsi que l'utilisation du très permissif .* qui peut générer pas mal de backtracking.

    En ce qui concerne le lookahead des deux patterns, on peut encore l'améliorer en abrégeant ses souffrances en cas d'échec, si on utilise le "verbe de contrôle du backtracking" (backtracking control verb) (*COMMIT):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    (?:
        \G(?!\A)/
      |
        \A /Services/(?'var'[[:alnum:]_-]++)(?=[[:alnum:]/_-]*+(*COMMIT)\z)/?
    )
    (?'params'[[:alnum:]_-]+)?
    Celui-ci fait échouer la pattern immédiatement et met fin à toute tergiversation du moteur si la sous pattern après lui échoue (Donc dans ce cas précis, si la fin de la chaîne \z ne se trouve pas immédiatement après lui), ce qui fait gagner une bonne vingtaine d'étapes inutiles.

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

Discussions similaires

  1. Non-Virtual-Interface pattern et Python
    Par nikopol82 dans le forum Général Python
    Réponses: 9
    Dernier message: 19/02/2012, 18h25
  2. Réponses: 3
    Dernier message: 21/02/2010, 19h09
  3. Réponses: 2
    Dernier message: 07/06/2008, 07h50
  4. [RegEx] Quelques patterns non détectés
    Par guidav dans le forum Langage
    Réponses: 1
    Dernier message: 02/01/2007, 21h15

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