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 :

Regex compliqué en PHP [PHP 5.4]


Sujet :

Langage PHP

  1. #1
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2020
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2020
    Messages : 14
    Points : 10
    Points
    10
    Par défaut Regex compliqué en PHP
    Bonjour,

    J'ai passé des heures là-dessus et j'ai cherché sur de nombreux forums mais pas de résultat.

    Je voudrais transformer la ligne suivante (en PHP avec la fonction preg_replace) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ("it's a good day today" OR sun) AND sky AND NOT rain
    pour la transformer en une requête MySQL :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (field LIKE "%it's a good day today%" OR field LIKE "%sun%") AND field like "%sky%" AND field NOT LIKE "%rain%"
    Il me faut donc "catcher" et remplacer seulement les mots clefs (sun sky rain), ainsi que les expressions entre guillemets ("it's a good day today"), et pas les parenthèses ni ces mots spéciaux : AND OR NOT

    J'ai essayé des patterns du genre ((?!OR|AND|NOT).)* et d'autres mais ça ne fonctionne pas.

    Toute aide est bienvenue.
    Merci.

  2. #2
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 895
    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 895
    Points : 6 651
    Points
    6 651
    Par défaut
    Tout d'abord une petite mise à jour s'impose: voir Unsupported Branches.

    Sans chercher la regex qui fait le café, un truc assez facile à mettre en place est une pattern qui récupère tout sans trop se poser de questions qu'on utilise avec preg_match_all. Il suffit d'utiliser des groupes de captures pour les parties à modifier, puis on fait une boucle pour reconstituer la chaîne.
    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
    $str = '("it\'s a good day today" OR sun) AND sky AND NOT rain';
     
    $pattern = '~ (?: \s+ (?:and|or|not) )* \s+ | [()] | " ( [^"]* ) " | ( [^"()\s]+ ) ~ix';
     
    if ( preg_match_all($pattern, $str, $matches, PREG_SET_ORDER) ) {
        $result = '';
        foreach ($matches as $m) {
            $last_group = count($m) - 1;
            if ( $last_group ) {
                $result .= 'field LIKE "%' . $m[$last_group] . '%"';
            } else {
                $result .= $m[0];
            }
        }
        echo $result;
    }
    Ça ne demande pas de connaissances délirantes en regex et ça fait le boulot.

    Maintenant on peut aussi le faire avec preg_replace mais c'est plus complexe:
    • avec des lookarounds (tests avant, arrière):
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      $pattern = '~ (?<! [^\s()] ) (?! or \s | and \s | not \s ) (?| " ([^"]*) " | ([^\s()]+) ) ~ix';
       
      $result = preg_replace($pattern, 'field like "%$1%"', $str);
      On commence par tester avec le test arrière négatif (?<! [^\s()] ) que la position actuelle n'est pas précédée par un caractère qui ne soit pas un espace ou une parenthèse; ce qui veut dire que la position actuelle est précédée par une parenthèse, un espace ou le début de la chaîne: attention à la double négation. Ça permet de ne pas se retrouver au milieu d'un mot.
      Ensuite, on vérifie que la position n'est pas suivie par or, and not avec un test avant négatif. On précise bien après chaque mot un espace pour éviter de matcher par exemple ornithorynque (ce qui arrive bien plus souvent qu'on ne le croit).
      Une fois ces deux tests réussis, on peut décrire la suite en toute quiétude. Les deux cas de figure (entre quotes ou sans espaces) sont placés dans un groupe branch reset dont l'avantage est de donner le même numéro aux groupe de capture de chacune des branches. Ainsi dans les deux cas, on a à faire au groupe de capture 1.
    • avec le modificateur A (Anchored):
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      $pattern = '~ \s* (?: [()] | and \s | or \s | not \s )*+ \s* \K (?| " ([^"]*) " | ([^\s()]+) ) ~Aix';
      // idem
      Ce modificateur oblige toutes les correspondances à démarrer là où c'est arrêtée la précédente, et ce en partant du début de la chaîne. Une telle contrainte oblige donc à décrire ce qui sépare chaque cible. Pour exclure les parenthèses, les mots logiques, les espaces, il suffit de placer \K pour indiquer le début de la chaîne qu'on souhaite récupérer.
      Cette méthode est à mon avis bien plus élégante que la précédente en évitant de tester une voire deux assertions qui vont échouer à pas mal de positions jusqu'à en trouver une bonne. Cette dernière pattern n'échoue presque jamais (sauf à la fin de la chaîne).

  3. #3
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2020
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2020
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    Merci CosmoKnacki pour ton aide et pour le temps que tu as consacré à ma demande.

    Quand je teste ta solution sur regex101.com ça fonctionne nickel, en rajoutant l'option global (g)

    Mais dans mon programme le preg_replace ne retourne rien. Voici mon code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    $patterns = '/\s* (?: [()] | and \s | or \s | not \s )*+ \s* \K (?| " ([^"]*) " | ([^\s()]+) )/Aixg';
    $replace = 'field like "%$1%"';
    $filtre_motclef='("it\'s a good day today" OR sun) AND sky AND NOT rain';
     
    $expression = preg_replace($patterns, $replace, $filtre_motclef);
    Est-ce que ça peut être dû à ma version de PHP 5.4 trop ancienne ?
    J'ai peur de devoir modifier le code de toutes mes pages en changeant de version PHP mais un jour il faudra bien s'y mettre

  4. #4
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    pour les balises [CODE][/CODE], c'est l'icône # du menu

  5. #5
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 895
    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 895
    Points : 6 651
    Points
    6 651
    Par défaut
    Il n'y a pas de modificateur g en PHP. Le fait qu'une recherche soit globale ou pas est déterminé par la fonction choisie (preg_match_all ou preg_match). Pour ce qui est des fonctions de remplacement la recherche est globale par défaut et ne peut être limitée que par le 4e paramètre.

  6. #6
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2020
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2020
    Messages : 14
    Points : 10
    Points
    10
    Par défaut
    C'est pour ça que mes preg_replace avec g ne fonctionnaient pas. Enfin grâce à ton aide mon problème est résolu, merci !

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

Discussions similaires

  1. [RegEx] Conversion regex JavaScript en PHP
    Par dtcSearch dans le forum Langage
    Réponses: 3
    Dernier message: 30/10/2014, 15h14
  2. regex sur tableau php
    Par skflers61 dans le forum Langage
    Réponses: 3
    Dernier message: 29/04/2014, 11h36
  3. Regex comme en php
    Par Amybond dans le forum C++
    Réponses: 4
    Dernier message: 30/08/2007, 22h47
  4. [Regex] Reconnaissance tag PHP
    Par neuromencien dans le forum Web
    Réponses: 4
    Dernier message: 14/10/2006, 17h53
  5. [RegEx] php et javascript dans une regex
    Par grochenel dans le forum Langage
    Réponses: 7
    Dernier message: 06/12/2005, 22h21

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