Соответствие парному тегу с регулярным выражением
Я пытаюсь получить определенные теги с их содержимым из документа 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
Любые указатели относительно того, где я терпит неудачу?
Ответы
Ответ 1
... и ответ всегда один и тот же: HTML + regex не может быть сделано. Сожалею. Используйте библиотеку разбора HTML для вашей конкретной структуры. Или, если ваш документ будет содержать только действительный XHTML, возьмите подход XPath, предложенный дрожанием в комментарии.
Ответ 2
это может помочь
я нашел учебник по http://www.regular-expressions.info/examples.html
в котором упоминается захват пары строк, повторяющихся в заданном тексте.
предложение использовать? после. *, чтобы остановить его после первого появления конечной строки пары в тексте
Ответ 3
Это известная проблема с регулярным выражением - вы не можете сопоставлять пары. Соответствие либо жадное, в котором оно соответствует последнему, которое оно находит, либо не жадному, в котором оно соответствует первому. Вы не можете убедить регулярное выражение считать открывающие и закрывающие скобки.
Я бы рекомендовал загрузить его в DOM и использовать его. Если вы пытаетесь реализовать парсер HTML, я бы рекомендовал использовать regex для lex it, а затем левый-правый синтаксический анализатор для анализа вывода вашего лексера.
Ответ 4
благодаря @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);
regex note
-
s
: a .
в шаблоне соответствует всем символам, включая символы новой строки
- Ключ здесь состоит в том, что
(?:(?!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>
)
Ответ 5
Разбор 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)+
), вы будете придерживаться таких примеров. Этот код может обрабатывать их все (пожалуйста, посмотрите здесь) или можете легко адаптироваться к новым ситуациям.