Соответствующий тег пары с регулярным выражением

Я пытаюсь извлечь определенные теги с их содержимым из документа 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)+) вы будете застревать на таких примерах. Этот код может обрабатывать их все (пожалуйста, посмотрите здесь раньше) или может легко адаптироваться к новым ситуациям.

Другие вопросы по тегам