Справка по регулярным выражениям PHP с использованием отрицательного взгляда - PullRequest
1 голос
/ 04 августа 2009

Я пытаюсь написать простую функцию для закрытия отсутствующих тегов HTML с помощью PHP preg_replace.

Я думал, что это будет относительно просто, но по какой-то причине это не так.

Что я в основном пытаюсь сделать, так это закрыть отсутствующий тег в следующей строке:

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

Подход, который я использовал, заключается в том, чтобы использовать отрицательный взгляд сзади, чтобы найти открывающие теги td, которым не предшествуют открытые и правильно закрытые теги.

Например:

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

Я написал шаблон регулярного выражения бесчисленными разными способами, но безрезультатно. Проблема была в том, что я не могу сопоставить только один открытый тд с отсутствующим / ом, предшествующим ему, - скорее, он совпадает с несколькими из открытых тэгов.

Вот полный текст ввода:

<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>

Что-то происходит с отрицательным взглядом в PHP, о котором я не знаю, или я просто не выбрал правильный шаблон сопоставления?

Любая помощь будет высоко ценится.

Спасибо, John

Ответы [ 4 ]

3 голосов
/ 04 августа 2009

Записывая свой комментарий к вашему вопросу, я подумал: «Определенно, должно быть другое решение, не включающее какое-либо регулярное выражение, которое станет невозможно поддерживать» ...

Может быть, я нашел способ; взгляните на

Инструкция первых гласит (цитирование):

В отличие от загрузки XML, HTML не имеет быть в хорошей форме, чтобы загрузить.

А в руководстве второго написано:

Создает HTML-документ из DOM представление.


Попытка тех, кто предоставил недействительную HTML-строку, дает следующий пример:

$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();

И при запуске (из командной строки, чтобы избежать проблем с выходом из HTML для правильного отображения) , я получаю:

$ 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>

Который, переформатированный, дает:

<!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>

Пока не идеально, я допускаю (например, он не добавил никаких тегов <table>) , но, по крайней мере, теги теперь закрыты как следует ...

Возможны проблемы с тегами DOCTYPE и <html>; Вы, возможно, не хотите, чтобы те ... Взгляните на некоторые комментарии на странице руководства: они могут помочь вам; -)



РЕДАКТИРОВАТЬ после немного больше мысли:

Ваш "полный" пример генерирует некоторые предупреждения; может быть, вы можете немного привести в порядок свой «HTML», прежде чем вводить 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

В худшем случае вы можете замаскировать эти ошибки либо с помощью функции error_reporting до и после вызова функции, либо с помощью оператора @ ...
Однако я бы вообще не рекомендовал их: использовать их следует в крайних случаях - возможно, это ^^

Тем не менее, на самом деле результат выглядит неплохо:

<!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>


В заключение, как уже предлагали другие, может помочь настоящий HTML-инструмент / очиститель; -)

0 голосов
/ 04 августа 2009

Это регулярное выражение работает для меня:

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

Обратите внимание, что он работает только для однострочных строк. Я имею в виду, это работает для:

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

но не для:

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

Я действительно не знаю, как заставить его работать с модификатором "s". Если кто-то может объяснить мне, я ценю.

Вот мой пример:

<?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>)?@s','<th$1>$2</th>',$html);
echo $text;
?>

выход:

<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>
0 голосов
/ 04 августа 2009

Вы также можете использовать что-то вроде HTMLTidy или Очиститель HTML для автоматического исправления вашего HTML.

0 голосов
/ 04 августа 2009

Проблема заключалась в том, что я не могу сопоставить только одно открытое слово с пропущенным </th> перед ним, а скорее оно совпадает с несколькими открытыми тегами.

Звучит так, будто вы хотите, чтобы выражения совпадений не были жадными или ленивыми. Используйте '*?' и '+?' вместо '*' и '+', и он получит как можно меньше символов, чтобы получить совпадение, а не столько, сколько сможет.

...