2009-10-06 3 views
0

J'ai besoin d'une regex (pour travailler en PHP) pour remplacer les mots anglais américains en HTML par des mots anglais britanniques. Donc, la couleur serait remplacée par la couleur, mètres par mètres et ainsi de suite [Je sais que les compteurs sont aussi un mot anglais britannique, mais pour la copie que nous utiliserons, nous ferons toujours référence aux unités de distance plutôt qu'aux appareils de mesure]. Le modèle aurait besoin de travailler avec précision dans les domaines suivants (un peu contrived) exemples (bien que je ne contrôle pas l'entrée réelle ceux-ci pourraient exister):De quel motif regex ai-je besoin pour cela?

<span style="color:red">This is the color red</span> 

[ne doit pas remplacer la couleur dans la balise HTML, mais devrait remplacer dans la phrase]

<p>Color: red</p> 

[devrait remplacer mot]

<p>Tony Brammeter lives 2000 meters from his sister</p> 

[devrait remplacer les compteurs pour le mot, mais pas au nom]

Je sais qu'il y a des cas limites où le remplacement ne serait pas utile (si son nom était Tony Meter par exemple), mais ceux-ci sont assez rares pour que nous puissions les traiter quand ils se présentent.

Répondre

5

Html/xml ne devrait pas être traité avec des expressions régulières, il est vraiment difficile de générer un qui correspondra anything. Mais vous pouvez utiliser la fonction interne dom extension et traiter votre chaîne récursive:

# Warning: untested code! 
function process($node, $replaceRules) { 
    foreach ($node->children as $childNode) { 
     if ($childNode instanceof DOMTextNode) { 
      $text = pre_replace(
       array_keys(replaceRules), 
       array_values($replaceRules), 
       $childNode->wholeText 
      ); 
      $node->replaceChild($childNode, new DOMTextNode($text)); 
     } else { 
      process($childNode, $replaceRules); 
     } 
    } 
} 
$replaceRules = array(
    '/\bcolor\b/i' => 'colour', 
    '/\bmeter\b/i' => 'metre', 
); 
$doc = new DOMDocument(); 
$doc->loadHtml($htmlString); 
process($doc, $replaceRules); 
$htmlString = $doc->saveHTML(); 
+0

Cool. Cela semble avoir bien fonctionné. J'ai dû apporter quelques modifications au code pour le faire fonctionner (DOMTextNode n'a pas fonctionné pour moi, tandis que DOMText l'a fait, en échangeant les arguments autour de $ node-> replaceChild etc), mais jusqu'à présent cela semble avoir bien fonctionné. Le seul petit problème est que je veux faire cela sur les chaînes, et l'utilisation de nouveaux DOMDocument transforme la chaîne en une page HTML avec un doctype etf enveloppé dans les balises html et body. Je peux supprimer cela en utilisant standard str_replace etc (ou, mais y at-il un meilleur moyen qui ne les crée pas en premier lieu? – Apemantus

0

Vous n'avez pas besoin d'utiliser explicitement une regex. Vous pouvez essayer la fonction str_replace, ou si vous avez besoin d'être insensible à la casse, utilisez la fonction str_ireplace.

Exemple:

$str = "<p>Color: red</p>"; 
$new_str = str_ireplace ('%color%', 'colour', $str); 

Vous pouvez passer un tableau avec tous les mots que vous voulez rechercher, au lieu de la chaîne.

+0

Sauf que je suis assez sûr que échouerait # 1 et # 3 de ses exemples; ce dernier aurait besoin d'une vérification des limites de mots ('\ bword \ b' dans une regex basée sur PCRE), et la première aurait au moins une vérification primitive de la balise. – Twisol

4

Je pense que vous préférez un dictionnaire et peut-être même une analyse grammaticale pour que cela fonctionne correctement, puisque vous n'avez aucun contrôle sur l'entrée. Une solution regex pure ne va pas vraiment être capable de traiter correctement ce type de données.

Donc, je suggère de trouver d'abord une liste de mots qui doivent être remplacés, ce ne sont pas seulement "couleur" et "mètre". Wikipedia has some information on the topic.

1

Vous ne voulez pas d'expression régulière pour cela. Les expressions régulières sont par nature sans état, et vous avez besoin d'une certaine mesure d'état pour pouvoir faire la différence entre «dans une balise html» et «dans les données».

Vous voulez utiliser un analyseur HTML en combinaison avec quelque chose comme str_replace, ou mieux encore, utiliser un dictionnaire de grammaire approprié et tout ce que Lucero suggère.

1

Le deuxième problème est plus facile - vous voulez remplacer quand il y a des limites de mot autour du mot: - ceci s'assurera que vous ne remplacez pas le mètre dans le brammètre.

Le premier problème est beaucoup plus difficile. Vous ne voulez pas remplacer les mots à l'intérieur des entités HTML - rien entre <> caractères. Donc, votre match doit s'assurer que vous avez vu> ou rien pour la dernière fois, mais jamais juste <. C'est soit difficile, et nécessite une combinaison de lookahead/lookbehind assertions, ou tout simplement impossible avec des expressions régulières. Un script implémentant une machine d'état fonctionnerait beaucoup mieux ici.

Questions connexes