Соответствующий тег пары с регулярным выражением
Я пытаюсь извлечь определенные теги с их содержимым из документа xhtml, но они соответствуют неверным конечным тегам.
В следующем содержании:
<cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
Конечный тег content_block для id="welcome" фактически совпадает с конечным тегом первого открывающего тега content_block.
Я использую регулярное выражение:
/<content_block id="(.*)">([\w\W]*?)<\/content_block>/i
Какие-нибудь указатели относительно того, где я терплю неудачу?
5 ответов
... и ответ всегда один и тот же: HTML + регулярное выражение не может быть сделано. Сожалею. Используйте HTML-библиотеку для анализа вашей конкретной платформы. Или, если ваш документ гарантированно содержит только действительный XHTML, используйте подход XPath, предложенный джиттером в комментарии.
Это может помочь, я нашел учебник на http://www.regular-expressions.info/examples.html котором упоминается захват пары строк, повторяющихся в данном тексте. предложение использовать? after .* чтобы остановить его после первого появления конечной строки пары в тексте
Это известная проблема с регулярным выражением - вы не можете сопоставить пары. Соответствие является либо жадным, в котором оно совпадает с последним найденным, либо не жадным, в котором оно совпадает с первым. Вы не можете убедить регулярное выражение считать открывающие и закрывающие скобки.
Я бы порекомендовал загрузить его в DOM и использовать его. Если вы пытаетесь реализовать HTML-парсер, я бы порекомендовал использовать регулярное выражение для его лексирования, а затем лево-правый парсер для анализа выходных данных вашего лексера.
Благодаря @Jan Żankowski и @ikegami их ответ вдохновил меня
Позвольте мне использовать PHP для демонстрации кода
<?php
$xml = <<<EOT
<cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
EOT;
preg_match('/<cache_namespace[^>]+>((?:(?!(<\/?div>)).)*)<\/cache_namespace>/s', $xml, $matches);
print_r($matches);
примечание регулярного выражения
s
вариант:.
в шаблоне совпадают все символы, включая переводы строки- Ключевым моментом здесь является то, что
(?:(?!STRING).)*
это строки как[^CHAR]*
это персонажам
результат
Array
(
[0] => <cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
[1] =>
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
)
Разбор XHTML или XML не сложно. Я предположил, что у вас есть правильный или правильно сформированный код.
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;
my $xml = <<"EOF";
<cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
EOF
while ($xml =~ m!
<(content_block)\sid="welcome"> # Start tag definition.
(\s* # It may consists of
(?: <\!--.*?--> # - comment
| [^<]* # - text
| <[^>]+/> # - another closed tag
| <\s*(\w+)[^>]*> # - another tag with some content
(?2)+ # (recursive definition of possible tag content)
</\3>
)
)*
</\1>
!sxgc) {
print "==> $&\n\n";
}
Пожалуйста, измените определение начального тега для другого контента (например, <\s*(\w+)[^>]*+>
). В любом случае, это хорошая отправная точка.
Если вы не будете использовать рекурсию (строка с (?2)+
) вы будете застревать на таких примерах. Этот код может обрабатывать их все (пожалуйста, посмотрите здесь раньше) или может легко адаптироваться к новым ситуациям.