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

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