Problème REGEX

  • Auteur de la discussion Auteur de la discussion 7804j
  • Date de début Date de début
WRInaute discret
Bonjour

En essayant de programmer un système de création de liens automatiques, je me suis aperçu que dans certains cas les liens ne se créaient pas...

En essayant de décomposer mon code au maximum, j'ai isolé une ligne et je l'ai légèrement modifiée pour en obtenir deux variantes. La première des deux ne crée pas de lien tandis que la seconde oui alors qu'elles devraient normalement le faire les deux... La seule différence est la présence des <> dans la chaîne.

Voilà donc les deux lignes :

Code:
echo preg_replace("#(?<!(?:src|alt|img)=(?:\"|'))". preg_quote("Amayiro", "#")."(?!(?:(?!</?img).)*>)#i", '<a href="/quetes/Ned-le-dentiste-140.html">Amayiro</a>',"ournengbgfdz voir Amayiro à afs ewrg  r er g rgla <> er rgfdm esr s sg seg sgilice de Bonta en [-33,-56].Il");
	
	echo preg_replace("#(?<!(?:src|alt|img)=(?:\"|'))". preg_quote("Amayiro", "#")."(?!(?:(?!</?img).)*>)#i", '<a href="/quetes/Ned-le-dentiste-140.html">Amayiro</a>',"ournengbgfdz voir Amayiro à afs ewrg  r er g rgla br er rgfdm esr s sg seg sgilice de Bonta en [-33,-56].Il");


Merci d'avance si quelqu'un aurait une explication :/
 
WRInaute occasionnel
Bonsoir,

Je pense que l'idée est de mettre Amayiro dans un lien. Si c'est le cas, pourquoi ne pas employer un str_replace au lieu de preg_replace ? Si la raison est parce qu'il est possible que Amayiro se trouve déjà être le texte d'une ancre, je ne suis sûr que le preg_quote soit vraiment utile puisqu'il sert à échapper les caractères spéciaux d'une chaîne générée par une capture (si mes souvenirs sont bons). Enfin, pour une meilleure compréhension de l'erreur, il serait bon d'étudier la gourmandise des quantifieurs, les parenthèses non-capturantes car à mon avis l'erreur vient de là...

J'espère avoir aidé.
 
WRInaute discret
Effectivement, le but est de mettre Amayiro dans un lien.

Maintenant non, ce n'est pas possible d'utiliser str_replace car il arrive que "Amayiro" soit déjà dans un lien ou tout simplement dans, par exemple, un balise "title" ce qui fait buguer aussi.

Bien sûr, dans ce cas-là preg_quote est inutile. Maintenant, il faut savoir qu'ordinairement "Amayiro" a une valeur variable qui peut contenir des caractères spéciaux, raison du preg_quote.

Maintenant je ne vois pas trop comment la gourmandise des quantificateurs, les parenthèses non-capturantes etc. peuvent empêcher le code de fonctionner correctement et c'est donc volontiers que j'accepterais des explications supplémentaires ;)
 
WRInaute accro
Moi les look-aheads ça me donne mal à la tête... :-)

En clair, c'est quoi le but exact de ta regexp? Trouver le texte en question, et le remplacer par un lien, sauf si le texte en question apparaît au début d'un attribut src, alt ou img (?!), et sauf si il est suivi par... euh... un truc bizarre qui à mon avis ne fait pas ce que tu veux, parce que ça "matche", entre autres, ".*>" (ce qui explique que ton premier exemple ne fonctionne pas, puisqu'il y a un > plus loin).

Le premier truc (mais je dis ça parce que je programme en perl et pas en php), c'est que tu serais nettement plus tranquille avec un vrai parser HTML qu'avec des regex, mais bon.

Le deuxième point, c'est que tu as du HTML, et qu'il ne devrait donc pas contenir du texte comme "<>". Soit c'est un tag HTML complet, soit ça devrait être des entités ("&lt;&gt;" ou au minimum "&lt;>"). Mais bon, comme le problème c'est le > et pas le <, ça ne devrait pas changer grand chose.

Sans chercher beaucoup plus (mais je pense que ça "casserait" dans de nombreux autres cas), je virerais juste tout le look-ahead (la partie après le preg_quote). Sinon, quelque chose comme (?![^<]*>), qui permet de vérifier qu'il n'y a pas un > qui suit sans être précédé d'un < (bref, un nouveau tag qui commence) devrait faire l'affaire. Mais si les > ne sont pas remplacés par les entités correspondantes (ce qui n'est pas obligatoire contrairement au < et au & si je ne m'abuse), ça ne marchera pas.

Sinon, je suppose que ton but en fait c'est de ne pas toucher aux balises HTML, mais uniquement au "texte". Dans ce cas, ta regex ne va pas gérer le cas où le mot apparaît dans un attribut (ailleurs qu'au début de celui-ci), ou dans un attribut autre que ceux que tu as cités, voire s'il apparaît dans le nom de l'attribut).

Je te propose plutôt ça:
preg_replace("#\G((?:[^<]*?<[^>]*>|[^<]*?)*?)".preg_quote($mot,"#")."#", '$1<a href="machin">truc</a>');

J'ai pas testé en php, mais en perl ça a l'air de marcher :-)

Note que tu devrais probablement aussi ajouter des \b autour du mot que tu cherches, histoire de ne pas attraper des "bouts" de mot.

Jacques.
 
WRInaute discret
Merci beaucoup pour ta longue réponse et désolé de ne pas avoir pu répondre plus tôt.

Oui, effectivement, ma REGEX ne pouvait pas fonctionner. J'ai donc essayé la tienne mais, apparemment, je n'ai pas réussi non plus à la faire marcher (avec un truc comme ça, si c'est bien ce que tu voulais dire : )
Code:
 echo preg_replace("#\G((?:[^<]*?<[^>]*>|[^<]*?)*?)".preg_quote("Amayiro","#")."#", '$1<a href="machin">Amayiro</a>',"ournengbgfdz voir Amayiro à afs ewrg  r er g rgla br er rgfdm esr s sg seg sgilice de Bonta en [-33,-56].Il");

Après m'être donc bien cassé la tête, parce que les regex c'est pas quelque chose qui coule de source pour moi, j'ai fait un truc du genre : http://regexr.com?2v0oq

Comme tu peux le voir dans mon lien, c'est une regex plutôt courte qui prend en compte tout ce qui ne fait pas partie d'un tag html.

Le seul problème de cette regex, c'est que les occurrences d'Amayiro qui se trouvent déjà dans un lien sont prises en compte aussi...

J'ai donc essayé un truc du genre mais sans trop de succès... : http://regexr.com?2v0ot

Le problème, c'est quand je fais mon assertion en demandant une deuxième condition : je ne sais pas comment m'y prendre pour éviter de devoir placer une assertion dans une assertion et en plus, ça n'a pas l'air de fonctionner :/

Voilà, merci d'avance si tu aurais une idée ;)
 
WRInaute accro
Visiblement c'est parce que php (pcre) ne supporte pas \G.

Ceci dit, ma regex ne gérait pas le cas où le mot cherché est déjà l'ancre d'un lien (par opposition à un attribut).

Ce qui me fait dire à nouveau, c'est que la bonne méthode c'est d'utiliser un vrai parser HTML, soit DOMDocument::loadHTML, soit PHP Simple HTML DOM Parser par exemple. Tu pourras alors parcourir tout l'arbre des éléments, sauter les <a>, et ne modifier que le texte.

Jacques.
 
WRInaute discret
Oui, je connais PHP Simple HTML DOM Parser mais je pense que cela serait aussi très compliqué, voire très lent, non ? :/

Etant donné que j'ai dans les 100 000 remplacements à faire par article et que j'ai des milliers d'articles, plus c'est rapide mieux c'est ^^'

Est-ce que tu penses que tu arriverais faire une regex qui conviendrait en améliorant celle-là : http://regexr.com?2v0oq ? Ou est-ce que tu penses qu'un Parser donnerait vraiment lieu à un traitement plus rapide ?
 
WRInaute accro
Le parser sera probablement plus lent, mais ce sera nettement plus fiable. Ce sera probablement plus rapide avec DOMDocument vu que ça repose sur libxml tandis que l'autre est (je crois?) en pur php.

Si c'est du "one shot", c'est quand même pas très grave si ça prend un peu plus de temps, si?

Jacques.
 
WRInaute discret
Fiable ? Bah il me semble que, à partir du moment où la regex est correct, c'est fiable. Maintenant, c'est vrai que pour ce qui est d'en trouver une correct...

Mais je crois que je vais privilégier la rapidité parce que dans ce cas précis elle est extrêmement importante :/

Enfin, merci quand même pour ta précieuse aide :D
 

➡️ Offre MyRankingMetrics ⬅️

pré-audit SEO gratuit avec RM Tech (+ avis d'expert)
coaching offert aux clients (avec Olivier Duffez ou Fabien Faceries)

Voir les détails ici

coaching SEO
Discussions similaires
Haut