2009-08-03 9 views
1

J'essaie d'écrire une fonction simple pour fermer les balises HTML manquantes en utilisant PHP preg_replace.Aide avec les expressions rationnelles PHP utilisant un aspect négatif derrière

Je pensais que ce serait relativement simple, mais pour une raison quelconque, il ne l'a pas été.

Ce que je veux essentiellement essayer de faire est proche d'une balise manquante dans la ligne suivante:

<tr> 
<th class="ProfileIndent0"> 
<p>Global pharmaceuticals</p> 
<td>197.2</td> 
<td>94</td> 
</tr> 

L'approche que je prends est d'utiliser un regard négatif derrière pour trouver l'ouverture des balises td qui sont non précédé par th ouvert et correctement fermé les étiquettes.

Par exemple:

$text = preg_replace('!<th(\s\S*){0,1}?>(.*)((?<!<\/th>)[\s]*<td>)!U','<th$1>$2</th>',$text); 

J'ai écrit les motifs d'expression régulière d'innombrables façons différentes sans succès. Le problème a été que je ne peux pas sembler correspondre seulement sur le td ouvert avec le/ième manquant qui le précède - mais plutôt il semble correspondre sur plusieurs des balises td ouvertes.

Voici le texte d'entrée complète:

<CO_TEXT text_type_id="6"> 
     <TEXT_DATA><![CDATA[<table class="ProfileChart"> <tr> <th class="TableHead" colspan="21">2008 Sales</th> </tr> 

<tr> <th class="ProfileIndent0"></th> <th class="ProfileHead">$ mil.</th> <th class="ProfileHead">% of total</th> </tr> 

<tr> <th class="ProfileIndent0"> <p>Global pharmaceuticals</p> <td>197.2</td> <td>94</td> </tr> 

<tr> <th class="ProfileIndent0">Impax pharmaceuticals</th> <td>12.9</td> <td>6</td> </tr> 

<tr> <th class="ProfileTotal">Total</th> <td class="ProfileDataTotal">210.1</td> <td class="ProfileDataTotal">100</td> </tr> </table><h3>Selected Generic Products</h3><ul class="prodoplist"><li>Anagrelide hydrochloride (generic Agrylin, thrombocytosis)</li><li>Bupropion hydr ochloride (generic Wellbutrin SR, depression)</li><li>Colestipol hydrochloride (generic Colestid, high cholesterol)</li><li>Dantrolene sodium (generic Dantrium, spasticity)</li><li>Metformin Hcl (generic Glucophage XR, diabetes)</li><li>Nadolol/Bendroflumethiazide (generic Corzide, hypertension)</li 
><li>Oxybutynin chloride (generic Ditropan XL, urinary incontinence, with Teva)</li><li>Oxycodone hydrochloride (generic OxyContin controlled release, pain)</li><li>Pilocarpine hydrochlorine (generic Salagen, dry mouth caused by radiation therapy)</li></ul>]]></TEXT_DATA> </CO_TEXT> 

Y at-il quelque chose avec behinds regard négatif en PHP que je ne suis pas au courant, ou ai-je tout simplement pas frappé à droite modèle correspondant?

Toute aide serait grandement appréciée.

Merci, John

+0

Salut! (désolé, ce n'est pas une réponse, juste une pensée, peut-être que cela vous aidera à penser qu'il pourrait y avoir d'autres façons de le faire.) En regardant votre regex, il n'y a qu'une seule chose qui me vient à l'esprit. bon outil "pour ce que vous essayez de faire ... Il est déjà difficile de lire une regex, et je n'imagine pas le désordre qu'il faudra pour être capable de gérer tout type de pseudo foiré -HTML on pourrait le nourrir ... –

+0

Pascal, oui - je sais ce que vous dites. Après avoir cogné la tête contre le mur ces derniers jours, je pense qu'il y a une meilleure façon de régler le problème. En particulier, attraper le mauvais HTML à la source - plutôt que la fin de l'affichage. – John

Répondre

0

Le problème est que je ne peux pas sembler correspondre à seulement celui td ouvert avec le </th> manquant précède - mais plutôt il semble correspondre à plusieurs des balises td ouvert .

On dirait que vous voulez des expressions de correspondance «non gourmandes» ou «paresseuses». Utilisez '*?' et '+?' au lieu de '*' et '+', et il va attraper le moins de caractères possible pour obtenir une correspondance, plutôt que de le faire autant que possible.

+0

Merci Alan. J'ai essayé d'ajouter un? dans les endroits appropriés, mais cela ne semblait pas faire la différence. – John

3

Écrire mon commentaire à votre question, je pensais « il y a definitly être obtenu une autre solution qui ne nécessite pas une sorte de regex qui deviendra impossible de maintenir » ...

Peut-être que j'ai trouvé un moyen ; jetez un oeil à

Le manuel du premier états (citant):

Contrairement à XML, le HTML n'a pas être bien formé pour charger.

et le manuel du second dit:

Crée un document HTML à partir de la représentation DOM .


Essayer ceux avec la chaîne HTML non valide que vous avez fourni donne cet exemple:

$str = <<<STRING 
<tr> 
<th class="ProfileIndent0"> 
<p>Global pharmaceuticals</p> 
<td>197.2</td> 
<td>94</td> 
</tr> 
STRING; 

$doc = new DOMDocument(); 
$doc->loadHTML($str); 
echo $doc->saveHTML(); 

Et, lors de l'exécution, il (de la ligne de commande, pour éviter tout problème à l'évacuation HTML pour l'obtenir correctement affiché), je reçois:

$ php ./temp.php 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> 
<html><body><tr> 
<th class="ProfileIndent0"> 
<p>Global pharmaceuticals</p> 
</th> 
<td>197.2</td> 
<td>94</td> 
</tr></body></html> 

qui, reformaté, REND:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" 
    "http://www.w3.org/TR/REC-html40/loose.dtd"> 
<html> 
    <body> 
     <tr> 
      <th class="ProfileIndent0"> 
       <p>Global pharmaceuticals</p> 
      </th> 
      <td>197.2</td> 
      <td>94</td> 
     </tr> 
    </body> 
</html> 

Pas encore parfait, je l'avoue (il n'a pas ajouté de <table> balises, par exemple), mais, au moins, les balises sont maintenant fermées comme devrait ...

Il pourrait y avoir certains problèmes avec les étiquettes DOCTYPE et <html>; vous pourriez ne pas vouloir les ... Jetez un oeil à somecomments sous la page de manuel: ils pourraient vous aider ;-)



EDIT après un peu plus de réflexion:

Votre l'exemple "complet" génère des avertissements; peut-être vous pouvez ranger votre « HTML » un peu avant l'alimentation ot à loadHTML ...

Warning: DOMDocument::loadHTML(): Tag co_text invalid in Entity, 
    line: 1 in /home/squale/developpement/tests/temp/temp.php on line 18 
Warning: DOMDocument::loadHTML(): Tag text_data invalid in Entity, 
    line: 2 in /home/squale/developpement/tests/temp/temp.php on line 18 
Warning: DOMDocument::loadHTML(): htmlParseStartTag: invalid element name in Entity, 
    line: 2 in /home/squale/developpement/tests/temp/temp.php on line 18 
Warning: DOMDocument::loadHTML(): Unexpected end tag : table in Entity, 
    line: 10 in /home/squale/developpement/tests/temp/temp.php on line 18 

Au pire, vous pouvez masquer ces erreurs, que ce soit en utilisant la fonction error_reporting avant et après appel de la fonction, ou en utilisant la @ operator ...
Je ne recommanderais pas généralement ceux-ci, cependant: l'utilisation de ceux-ci devraient être dans les cas extrêmes - peut-être celui-ci encore ^^

, le résultat ne cherche pas à mal, en fait:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" 
    "http://www.w3.org/TR/REC-html40/loose.dtd"> 
<html> 
<body> 
    <co_text text_type_id="6"> 
     <text_data> 
      <tr> 
       <th class="TableHead" colspan="21">2008 Sales</th> 
      </tr> 
      <tr> 
       <th class="ProfileIndent0"></th> 
       <th class="ProfileHead">$ mil.</th> 
       <th class="ProfileHead">% of total</th> 
      </tr> 
      <tr> 
       <th class="ProfileIndent0"> <p>Global pharmaceuticals</p> </th> 
       <td>197.2</td> 
       <td>94</td> 
      </tr> 
      <tr> 
       <th class="ProfileIndent0">Impax pharmaceuticals</th> 
       <td>12.9</td> 
       <td>6</td> 
      </tr> 
      <tr> 
       <th class="ProfileTotal">Total</th> 
       <td class="ProfileDataTotal">210.1</td> 
       <td class="ProfileDataTotal">100</td> 
      </tr> 
      <h3>Selected Generic Products</h3> 
      <ul class="prodoplist"> 
       <li>Anagrelide hydrochloride (generic Agrylin, thrombocytosis)</li> 
       <li>Bupropion hydr ochloride (generic Wellbutrin SR, depression)</li> 
       <li>Colestipol hydrochloride (generic Colestid, high cholesterol)</li> 
       <li>Dantrolene sodium (generic Dantrium, spasticity)</li> 
       <li>Metformin Hcl (generic Glucophage XR, diabetes)</li> 
       <li>Nadolol/Bendroflumethiazide (generic Corzide, hypertension)</li> 
       <li>Oxybutynin chloride (generic Ditropan XL, urinary incontinence, with Teva)</li> 
       <li>Oxycodone hydrochloride (generic OxyContin controlled release, pain)</li> 
       <li>Pilocarpine hydrochlorine (generic Salagen, dry mouth caused by radiation therapy)</li> 
      </ul> 
     ]]&gt; 
     </text_data> 
    </co_text> 
</body> 
</html> 


Pour conclure, comme d'autres déjà suggéré, un vrai HTML tidyier/purificateur pourrait être en mesure d'aider ;-)

+0

+1 - Je serais enclin à ranger la sortie avec un bon ordre, plutôt que de réinventer la roue sur une expression regex qui serait, comme quelqu'un d'autre mentionné, difficile à maintenir. – EvilChookie

+0

Merci beaucoup Pascal. J'avais précédemment tenté d'exécuter la chaîne via les fonctions PHP Tidy, mais bizarrement Tidy tente à tort de fermer la balise th en encapsulant incorrectement la ligne entière avec la balise th tout au long de la liste non ordonnée. – John

0

Vous pourriez également être en mesure d'utiliser quelque chose comme HTMLTidy ou HTML Purifier pour corriger automatiquement votre code HTML.

0

Ce regex travaille pour moi:

$text = preg_replace('@<th([^>]*)>(.*<\/td>)(<\/th>)[email protected]','<th$1>$2</th>',$text); 

Notez que pour que ce travail des lignes de ligne unique.Je veux dire, ça marche pour:

<tr><th><td>some</td></tr> 

mais pas pour:

<tr><th> 
<td>some</td> 
</tr> 

Je ne sais vraiment pas comment le faire fonctionner avec le modificateur « s ». Si quelqu'un pouvait m'expliquer, j'apprécie.

Voici mon exemple:

<?php 
$html = '<CO_TEXT text_type_id="6"> 
     <TEXT_DATA><![CDATA[<table class="ProfileChart"> <tr> <th class="TableHead" colspan="21">2008 Sales</th> </tr> 

<tr> <th class="ProfileIndent0"></th> <th class="ProfileHead">$ mil.</th> <th class="ProfileHead">% of total</th> </tr> 

<tr> <th class="ProfileIndent0"> <p>Global pharmaceuticals</p> <td>197.2</td> <td>94</td> </tr> 

<tr> <th class="ProfileIndent0">Impax pharmaceuticals</th> <td>12.9</td> <td>6</td> </tr> 

<tr> <th class="ProfileTotal">Total</th> <td class="ProfileDataTotal">210.1</td> <td class="ProfileDataTotal">100</td> </tr> </table><h3>Selected Generic Products</h3><ul class="prodoplist"><li>Anagrelide hydrochloride (generic Agrylin, thrombocytosis)</li><li>Bupropion hydr ochloride (generic Wellbutrin SR, depression)</li><li>Colestipol hydrochloride (generic Colestid, high cholesterol)</li><li>Dantrolene sodium (generic Dantrium, spasticity)</li><li>Metformin Hcl (generic Glucophage XR, diabetes)</li><li>Nadolol/Bendroflumethiazide (generic Corzide, hypertension)</li 
><li>Oxybutynin chloride (generic Ditropan XL, urinary incontinence, with Teva)</li><li>Oxycodone hydrochloride (generic OxyContin controlled release, pain)</li><li>Pilocarpine hydrochlorine (generic Salagen, dry mouth caused by radiation therapy)</li></ul>]]></TEXT_DATA> </CO_TEXT>'; 

$text = preg_replace('@<th([^>]*)>(.*<\/td>)(<\/th>)[email protected]','<th$1>$2</th>',$html); 
echo $text; 
?> 

sortie:

<CO_TEXT text_type_id="6"> 
     <TEXT_DATA><![CDATA[<table class="ProfileChart"> <tr> <th class="TableHead" colspan="21">2008 Sales</th> </tr> 

<tr> <th class="ProfileIndent0"></th> <th class="ProfileHead">$ mil.</th> <th class="ProfileHead">% of total</th> </tr> 

<tr> <th class="ProfileIndent0"> <p>Global pharmaceuticals</p> <td>197.2</td> <td>94</td> </tr> 

<tr> <th class="ProfileIndent0">Impax pharmaceuticals</th> <td>12.9</td> <td>6</td> </tr> 

<tr> <th class="ProfileTotal">Total</th> <td class="ProfileDataTotal">210.1</td> <td class="ProfileDataTotal">100</td></th> </tr> </table><h3>Selected Generic Products</h3><ul class="prodoplist"><li>Anagrelide hydrochloride (generic Agrylin, thrombocytosis)</li><li>Bupropion hydr ochloride (generic Wellbutrin SR, depression)</li><li>Colestipol hydrochloride (generic Colestid, high cholesterol)</li><li>Dantrolene sodium (generic Dantrium, spasticity)</li><li>Metformin Hcl (generic Glucophage XR, diabetes)</li><li>Nadolol/Bendroflumethiazide (generic Corzide, hypertension)</li 
><li>Oxybutynin chloride (generic Ditropan XL, urinary incontinence, with Teva)</li><li>Oxycodone hydrochloride (generic OxyContin controlled release, pain)</li><li>Pilocarpine hydrochlorine (generic Salagen, dry mouth caused by radiation therapy)</li></ul>]]></TEXT_DATA> </CO_TEXT>