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

  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.

  7. #7
    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
    Ton truc ça booste drastiquement les perfs !
    Surtout que cela pourra beaucoup jouer quand j'aurais plus de routes configurées. Pour mes tests je n'en ai encore que 3 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    [Services]
    pattern = "/Services/%{ServiceName}/+"
    controller = "\mg\services\%{ServiceName}Controller"
     
    [Mobile Terminal]
    pattern = "/Mobile/+"
    filter = "\mg\filters\IEM6Filter"
    controller = "\mg\MobileTerminalController"
     
    [HomePage]
    pattern = "/"
    controller = "\mg\HomeController"
    Les patterns sont générés dynamiquement à partir d'un fichier INI jusqu'à en trouver un qui match. Donc rejeter rapidement est un sacré plus.

    Que me conseillerais-tu comme lecture sur le sujet ? J'en ai consulté un certain nombre en anglais, mais comme c'est déjà un domaine très pointu il y a toujours des subtilités qu'il est difficile de saisir.

  8. #8
    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
    Il y a le livre Mastering regular expressions de Friedl chez O'REILLY (3e édition), les deux premières éditions sont trouvables gratuitement sur internet. C'est un livre généraliste sur les expressions régulières qui ne traite pas en particulier de PCRE, mais qui couvre les notions essentielles en profondeur, et qui révèle certaines techniques d'optimisation.

    Ensuite il y a le site rexegg.com, pas exhaustif du tout, mais qui lui est plutôt axé sur PCRE dont une bonne partie sur ses outils de derrière les fagots.

    Pour finir, il y a la documentation de PCRE. PHP n'utilise pas (pour le moment?) la nouvelle version appelée PCRE2, mais les versions 8.3x. Là bien sur, c'est exhaustif, mais par contre c'est assez avare en exemple.

    Les "backtracking control verbs" sont assez mal documentés, et il faut parfois aller chercher dans la documentation de Perl pour avoir plus de renseignements, mais attention, Perl et PCRE n'ont pas toujours le même comportement.

    Sinon, il faut aller chercher des informations ça et là, mais surtout faire des tests et comparer.

  9. #9
    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
    Merci beaucoup, maintenant j'ai matière à...

    Beaucoup de cours PHP conseillent d'éviter les regex quand c'est possible parce que mal maitrisées cela peut devenir une bombe à retardement dans une appli conséquente.
    Il suffit sinon de bucher un minimum pour bien en comprendre les mécanismes. Je n'ai plus qu'à m'y mettre sérieusement.

  10. #10
    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
    Les regex génèrent énormément de fantasmes de toutes natures dont la principale origine est la méconnaissance. D'où une palette de discours allant de "vade retro satanas" jusqu'à "avec les regex on peut tout faire".

    Dire "d'éviter les regex quand c'est possible parce que mal maitrisées cela peut devenir une bombe à retardement dans une appli conséquente" est tout simplement une ânerie dans la mesure ou n'importe quel bout de code mal écrit avec ou sans regex peut devenir une "bombe à retardement" (ou pas à retardement, d'ailleurs je ne vois pas ce que le retardement fait ici). Le fait même, qu'il y ait dans cette phrase une distinction entre "regex" et "code `normal`" révèle déjà la supercherie. Le tout agrémenté d'une image choc pour frapper l'esprit fragile du jeune programmeur innocent en le mettant face à son pire cauchemar: la bombe à retardement dans une "appli conséquente" (<-- attention c'est comme ça qu'on dit dans l'informatique.) Tous les ingrédients semblent réunis pour, comme on dit communément, "bourrer l'mou".

    N'importe quoi!

    Les regex ont la particularité de former un langage intégré de manière symbiotique à un autre langage. On ne les utilise pas seules, elles sont intégrées au langage hôte et le langage hôte en tire les avantages. Mais elles ne font pas exception, c'est également le cas d'autres technologies comme le DOM, XPath, XSLT, les chaînes formatées, etc. qui ne sont pas propres au langage hôte, et pourtant personne ne s'inquiétera si on utilise ces dernières! À noter également qu'aucune de ces technologies n'est indispensable.

    Une technologie s'utilise lorsqu'elle est pertinente et apporte un avantage par rapport à une autre approche. On choisit d'avoir recours à une technologie pour une raison pratique.

    L'apprentissage des regex n'est pas quelque chose d'immédiat. Comme les autres technologies sus-citées, on commence par une utilisation basique jusqu'au jour où on se plonge dans la documentation pour en savoir plus (par curiosité ou par nécessité, selon si on est prévoyant ou non) et en tirer tous les avantages. Mais du fait de leur grande expressivité (beaucoup d'informations en très peu de caractères), elles effarouchent le néophyte. Il est pourtant possible comme n'importe quel code de les commenter. Quoi qu'il en soit, certains désignent les regex comme une boite noire, quelque chose d'opaque sur lequel ils n'osent lever le voile.

    Si on regarde du coté de l'enseignement, c'est clair et net, c'est un peu la dernière roue du carrosse, le truc qu'on fait si on a le temps, que l'on survole ou qui se trouve dans la partie annexe d'un cours. Si on fait des sciences de l'informatique, on étudie les expressions régulières dans leur acception théorique, ce qui n'est pas une mauvaise chose, mais on se retrouve alors déconnecté de l'outil réel qui en dérive que l'on appelle couramment "expression régulière" ou "regex" par abus de langage du point de vu théorique et qui n'est pas tout à fait la même chose, d'où un amalgame qui est la source de confusions.

    Ce contexte donne lieu à toute une série d'affirmations fausses que l'on retrouve souvent et auxquelles des programmeurs dont certains avancés semblent donner crédit aveuglément:
    • "Les regex c'est lent": oui, c'est sûr si tu encadres un coup de boule ou un lancé de chat sur le clavier entre deux slashes, ça ne donne pas un résultat rapide. Oui si tu cherches à reproduire une fonction qui existe déjà (donc écrite et compilée en C pour ce qui est de PHP), là c'est sûr c'est plus lent. Par contre dés lors qu'un regex permet de remplacer plusieurs manipulations de texte (ou de données quelconques) ou évite certains tests , ce n'est plus le forcément le cas. Certains réfractaires sont parfois prêt à pondre jusqu'à 100 lignes de codes pour éviter d'utiliser une expression régulière en étant persuadés que le résultat sera plus rapide! Ce qui n'a aucun sens, d'autant plus pour un langage interprété.
      S'il y a un prix à payer avec les regex, c'est la mise en œuvre du moteur. Donc on ne le paye qu'une seule fois, et il faut aussi relativiser son coût et dans quel mesure il est amorti pour pouvoir le comparer à une autre solution. Il ne faut pas hésiter non plus à se lancer dans des solutions mixtes et ne pas être esclave d'une vision bipolaire avec regex/sans regex.
    • "Les regex c'est pas maintenable": N'importe quoi une fois de plus! Les regex sont censées être une connaissance acquise. Donc tout programmeur est censé savoir les lire et les éditer au même titre que telle ou telle fonction du langage dont il se prétend l'expert. Pourquoi devrait on tolérer l'impasse sur un domaine particulier du langage plutôt que sur un autre? D'autre part, une pattern a rarement besoin d'être éditée de part sa nature, mais plutôt d'être réécrite si le besoin change ou pour l'optimiser (Pour réécrire une pattern on part plus volontiers de l'objectif à atteindre plutôt que de ce qui a déjà été écrit, quitte à s'y référer dans un deuxième temps pour comparer). De plus, rien n'empêche comme pour n'importe quel autre code de l'agrémenter de commentaires et de la mettre en forme.
      Je ne suis pas sûr non plus qu'un paquet de nouilles à base de boucles, de tests, de strpos et de substr soit forcément plus digeste et mieux compréhensible comme certains se plaisent à le croire. Tout dépend de ce que l'on souhaite accomplir et encore une fois de la solution la mieux appropriée.
    • "On ne peut pas faire ci ou ça avec les regex pour une raison théorique": Là, c'est une pure récitation de cours théoriques mal compris ou mal appliqués. On cite des théorèmes, mais on s'est en réalité pris les pieds dans l'abus de langage décrit précédemment, tantôt par pure mauvaise foi, tantôt avec toute la bonne volonté du monde.

  11. #11
    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
    Bon, je vais expliciter ma pensée parce que tu n'as pas forcément compris où je voulais en venir.

    Les regex sont beaucoup plus dures à maitriser qu'un langage plus courant parce qu'ils sont on ne peut plus éloignés d'un langage naturel (c'est en même temps le propre des expressions rationnelles).
    Du fait de cette aspect non naturel du langage, les erreurs sont plus difficilement décelables en lecture rapide (ce qui s'impose dans une application conséquente : plus de lignes, moins de temps à consacrer à chacune). Et comme tu le dis, on se retrouve dans le même cas avec une imbrication de fonctions telles substr ou strpos.
    Je parle de "bombe à retardement" parce que, comme dans mon cas où tu as pointé du doigt un cas où le pattern provenant de StackOverflow ne fonctionnait pas tel qu'attendu, si tu ne maitrises pas complètement l'outil tu te retrouves avec des situations non communes dans lesquelles ton application ne va pas se comporter comme elle le devrait. Tu testes en ne cernant pas ces cas particuliers, tu mets ton appli en prod et là c'est le drame...

    Avec l'optimisation que tu as fait sur mon problème, cela démontre que la plupart des expressions rationnelles sont lentes, oui, mais parce qu'en général les développeurs s'arrêtent à "ça marche" alors que la première solution fonctionnelle est très loin d'être optimale. Encore une fois c'est dû à la méconnaissance résultant du manque d'enseignement du sujet en université (je n'y ai moi-même jamais été initié avant de d'en avoir besoin dans un projet alors que l'on étudiait la construction des langages fonctionnels).

  12. #12
    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
    "L'éloignement avec le langage naturel", disons le niveau d'abstraction n'est pas vraiment la difficulté des regex qui après tout contient assez peu de symboles et dont le but est uniquement descriptif. Dés lors qu'on connaît ces symboles et que l'on sait sur quel type de chaîne une pattern est censée s'appliquer, c'est même plutôt facile à lire.

    La vrai difficulté c'est de comprendre le fonctionnement du moteur à bracktracking. D'ailleurs ce n'est pas par hasard si énormément de questions portent sur des problèmes de quantificateurs greedy/non-greedy, ou on pour origine le simple fait qu'une chaîne est lue de la gauche vers la droite. C'est parce que ces notions essentielles ne sont pas acquises. En gros les gens connaissent les symboles, savent à quoi ils servent, mais n'ont aucune idée sur la manière dont ils sont traités.

    Maintenant, pour ce qui de débugger une grosse application, le problème n'est pas au niveau d'une regex ou de telle ou telle fonction, mais au niveau du découpage du code et s'il y a eu des tests unitaires au cours de l'écriture et des outils de débuggage. Si l'application est mal codée de ce point de vue, ça revient à chercher une aiguille dans une botte de foin et ce quelque soit l'erreur.

    Le développeur qui se contente du "ça marche" avec sa pattern sans chercher à prévoir et sans tester les cas limites a de toute manière la même attitude avec n'importe quelle autre partie du code. Donc pour lui quand "ça marche" vraiment, c'est par hasard. On ne peut pas tout savoir en programmation, le tout c'est de connaître ses limites et d'agir en conséquence, pas de jouer à la roulette avec le code. Il n'y a rien qui soit propre au regex la dedans.

+ 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