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 :

Remplacment de "mots"


Sujet :

Langage PHP

  1. #1
    Invité
    Invité(e)
    Par défaut Remplacment de "mots"
    Bonjour à tous,

    Je cherche à remplacer exactement une suite de caractère par une autre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $Chaine="RENVOYER .1.3.6 + 2 * .3.6.7";
    $NouvelleChaine=preg_replace('/\b.3.6\b/','a',$Chaine);
    echo $NouvelleChaine."\n";
    Retourne "RENVOYER .1a + 2 * .3.6.7"

    Et c'est là tout mon problème.
    Le remplacement ne devrait pas avoir lieu.
    Il ne devrait fonctionner que dans ce cas là
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $Chaine="RENVOYER .1.3.6 + 2 * .1.3.6.1";
    $NouvelleChaine=preg_replace('/\b.1.3.6\b/','a',$Chaine);
    echo $NouvelleChaine."\n";
    Et retourner "RENVOYER a + 2 * .3.6.7"

    En d'autres termes, comment changer ce que PHP considère être comme un "changement" de mot ?

    Merci

  2. #2
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    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 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Première chose, le point est un caractère spécial dans une regex qui signifie n'importe quel caractère à l'exception d'une nouvelle ligne. Pour signifier un point au sens littéral, il faut l'échapper:\. avec un antislash.

    Ce qu'on appelle \b (un "word boundary" ~ limite de mot) à un sens bien particulier dans une expression régulière. Il s'agit de la limite entre un caractère appartenant à la classe de caractères \w et tout ce qui n'est pas un caractère appartenant à \w, soit:
    • un caractère appartenant à \W
    • Le début de la chaîne
    • La fin de la chaîne


    À la base \w est l'équivalent de [A-Za-z0-9_] (ou quand le modificateur u est utilisé:[\pL\pN_]) donc le point (littéral) n'y figure pas. Donc si je teste la chaîne "3.6.2" avec la pattern /3\.6\b/, cette dernière va réussir puisque "6" appartient à \w et le point n'appartient pas à \w donc le word boundary \b est bien vérifié.

    Conclusion pour ce que tu cherches à délimiter, le word boundary n'est tout simplement pas approprié.

    La solution de repli consiste donc à définir explicitement ce qui sépare les éléments que tu cherches du reste. Donc c'est à toi d'être au clair sur ce point (et tu dois surtout bien envisager tous les cas de figures pour éviter les mauvaises surprises). C'est le plus gros du travail.

    Si par exemple je décide que ce qui sépare mon élément du reste est tout ce qui n'est ni un point ni un chiffre ascii (ce qui inclus également les limites de la chaîne), je peux écrire:


    Une petite remarque à propos des caractères spéciaux comme le point et de leur éventuel échappement: La plupart des caractères spéciaux n'ont plus besoin d'être échappé quand ils sont à l'intérieur d'une classe de caractère (ils y perdent leur sens spécial). C'est pour ça que j'ai écrit [0-9.] et pas [0-9\.].

    Il est aussi possible quand on a une pattern comportant des parties devant être prises au sens littéral, de les encadrer avec \Q et \E pour que les caractères spéciaux contenus ne soit pas interprétés :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $NouvelleChaine = preg_replace('/(?<![0-9.])\Q.1.3.6\E(?![0-9.])/', 'a', $Chaine);
    Attention tout de même car ça ne marche pas avec le délimiteur. Sinon une solution plus radicale:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $NouvelleChaine = preg_replace('/(?<![0-9.])' . preg_quote('.1.3.6', '/') . '(?![0-9.])/', 'a', $Chaine);
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  3. #3
    Invité
    Invité(e)
    Par défaut
    A grand merci à toi CosmoKnacki
    Tu maîtrises le regexp...
    Et c'est loin d'être mon cas...
    J'ai été particulièrement attentif à
    La solution de repli consiste donc à définir explicitement ce qui sépare les éléments que tu cherches du reste. Donc c'est à toi d'être au clair sur ce point (et tu dois surtout bien envisager tous les cas de figures pour éviter les mauvaises surprises). C'est le plus gros du travail.
    Et c'est là le drame.
    Il suffit que j'oublie un des caractères pour me retrouver dans la mouise.
    Je pense adopter une solution plus conservatrice et (comme j'ai la chance d'avoir la main sur la chaine "source") délimiter la chaine recherchée avec un # (enfin 2 : un au début et l'autre à la fin)
    Ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $Chaine="RENVOYER #.1.3.6# + 2 * #.1.3.6.1#";
    $NouvelleChaine=preg_replace(['/#.1.3.6#/','/#.1.3.6.1#/'],['toto','tata'],$Chaine);
    echo $NouvelleChaine."\n";
    Penses-tu que dans ce cas je sois à l'abri des surprises ?

  4. #4
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    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 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Bien sur si tu as la main sur la chaîne source et que tu peux faire ça, plus de problèmes! (hormis le fait que tu n'as échappé aucun point).

    D'ailleurs dans ce cas, pas besoin de regex du tout, tu peux utiliser str_replace ou strtr:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $result = str_replace(['#.1.3.6#', '#.1.3.6.1#'], ['toto', 'tata'], $source);
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $result = strtr($source, ['#.1.3.6#' => 'toto', '#.1.3.6.1#' => 'tata']);
    Ça n'en sera que plus rapide.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  5. #5
    Invité
    Invité(e)
    Par défaut
    Tu as parfaitement raison.
    Aucun besoin de regex dans ces conditions.

    Pour le point, je me suis fié à ce que tu as écrit :
    La plupart des caractères spéciaux n'ont plus besoin d'être échappé quand ils sont à l'intérieur d'une classe de caractère
    Ce qui semblait être le cas ici puisque on est dans la "classe" mais si celle-ci est figée.

    Il ne me reste plus qu'à te remercier du temps que tu m'as consacré et des tes excellents conseils "saucisse astrale"

  6. #6
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    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 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Ah bah non là t'était pas dans une classe de caractères.

    Les caractères spéciaux dans une pattern sont ( ) [ { . | \ ^ $ * + ?. Si par exemple tu veux trouver "(lapin)" dans une chaîne tu dois écrire /\(lapin\)/. Par contre, si tu veux trouver "(cochon)" ou "(cochon|" dans ce cas tu écris /\(cochon[|)]/. Là, | et ) sont dans une classe de caractères et n'ont pas besoin d'être échappés.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

Discussions similaires

  1. Réponses: 2
    Dernier message: 30/12/2010, 17h02

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