Отказ от ответственности: Не используйте регулярные выражения!
Не рекомендуется использовать регулярные выражения для разбора HTML (или любого другого нерегулярного языка). Есть много ловушек и способов решения проблемы. Тем не менее, мне очень нравится использовать регулярные выражения для решения сложных проблем, таких как эта, которая включает в себя вложенные структуры. Если кто-то другой предоставляет работающее решение без регулярных выражений, я бы порекомендовал вам использовать это вместо следующего.
Регулярное решение:
Следующее решение реализует рекурсивное регулярное выражение, которое используется вместе с функцией preg_replace_callback()
(которая вызывает себя рекурсивно, когда содержимое элемента FONT содержит вложенный элемент FONT). Регулярное выражение соответствует самому внешнему элементу FONT (который может содержать вложенные элементы FONT). Функция обратного вызова удаляет начальный и конечный теги только тех элементов FONT, которые не имеют атрибутов. Теги FONT, которые имеют атрибуты, сохраняются. Я думаю, вы найдете, что это делает довольно хорошую работу:
функция remove_font_tags_without_attr ($ text)
<?php // test.php Rev:20111219_1100
// Recursive regex matches an outermost FONT element and its contents.
$re = '% # Match outermost FONT element.
< # Start of HTML start tag
( # $1: FONT element start tag.
font # Tag name = FONT
( # $2: FONT start tag attributes.
(?: # Group for zero or more attributes.
\s+ # Required whitespace precedes attrib.
[\w.\-:]+ # Attribute name.
(?: # Group for optional attribute value.
\s*=\s* # Name and value separated by =
(?: # Group for value alternatives.
\'[^\']*\' # Either single quoted,
| "[^"]*" # or double quoted,
| [\w.\-:]+ # or unquoted value.
) # End group of value alternatives.
)? # Attribute value is optional.
)* # Zero or more attributes.
) # End $2: FONT start tag attributes.
\s* # Optional whitespace before closing >.
> # End FONT element start tag.
) # End $1: FONT element start tag.
( # $3: FONT element contents.
(?: # Group for zero or more content alts.
(?R) # Either a nested FONT element.
| # or non-FONT tag stuff.
[^<]* # {normal*} Non-< start of tag stuff.
(?: # Begin "unrolling-the-loop".
< # {special} A "<", but only if it is
(?:!/?font) # NOT start of a <font or </font
[^<]* # more {normal*} Non-< start of tag.
)* # End {(special normal*)*} construct.
)* # Zero or more content alternatives.
) # End $3: FONT element contents.
</font\s*> # FONT element end tag.
%xi';
// Remove matching start and end tags of FONT elements having no attributes.
function remove_font_tags_without_attr($text) {
global $re;
$text = preg_replace_callback($re,
'_remove_font_tags_without_attr_cb', $text);
$text = str_replace("<\0", '<', $text);
return $text;
}
function _remove_font_tags_without_attr_cb($matches) {
global $re;
if (preg_match($re, $matches[3])) {
$matches[3] = preg_replace_callback($re,
'_remove_font_tags_without_attr_cb', $matches[3]);
}
if ($matches[2] == '') { // If this FONT tag has no attributes,
return $matches[3]; // Then strip both start and end tag.
}
// Hide the start and end tags by inserting a temporary null char.
return "<\0". $matches[1] . $matches[3] . "<\0/font>";
}
$data = file_get_contents('testdata.html');
$output = remove_font_tags_without_attr($data);
file_put_contents('testdata_out.html', $output);
?>
Пример ввода:
<font attrib="value">
<font>
<font attrib="value">
<font>
<font attrib="value">
</font>
</font>
</font>
</font>
</font>
Пример вывода:
<font attrib="value">
<font attrib="value">
<font attrib="value">
</font>
</font>
</font>
Сложность регулярного выражения требуется для правильной обработки атрибутов тега, значения которых могут содержать <>
угловых скобок.