рекурсивное регулярное выражеие

Stur

Guest
рекурсивное регулярное выражеие

дело в следуйщем
надо чтобы регла обрабатывала правильно вложеные теги
в результирующий массив попадало
название тега, атрибуты тега если есть, и внутренний текст.
и еще, теги также могут быть одинарными типа такого
<clInfo border=1/> в этом случае находим название и атрибуты

например такой html
<div>
....текст1
<div>
.......текст2
</div>
.....текст3
</div>

.... текст4

<div>
....текст5
</div>

мой вариант
'{<(' . $tagName . ')([^>]*)(?:(?:/>)|(?:>(.*)</\1>))}Usi'
вместо tagName подставляется название искомого тега
в принципе она работает но не совсем правильно
два вложенных <DIV > обрабатываются неправильно регла захватывает ближайший закр. тег и отваливает вот так:
<div>
....текст1
<div>
.......текст2
</div>
если включить жадность (убрать U) тогда регла захватит вообще весь текст т.е. до последнего загрыв </div>

в мануле нашел такой пример рекурсивный
\( ( ( (?>[^()]+) | (?R) )* )\)
онт правильно обрабатывает вложеные + последовательные скобки
получается так что мне вместо вот этого ограничения [^()]
надо поставить название моего тега только как это сделать?
заранее спасибо.
 

Profic

just Profic (PHP5 BetaTeam)
Забудь. Рекурсивные регулярки на больших текстах любят ронять php и/или апач.
Тебе поможет
PHP:
$parseArray = preg_split ('~(</?' . $tagName . '[^>]*?/?>)~si', $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
print_r ($parseArray);
+ проход по получившемуся массиву
 

valyala

Новичок
Profic прав. Рекурсивные регулярные выражения в большинстве случаев лучше обходить стороной. Особенно если не до конца понимаешь принцип работы регулярных выражений, не знаком с устройством детерменированных и недетерменированных конечных автоматов, которые создаются при компиляции регулярных выражений и затем используются для поиска совпадений по заданному шаблону, а также не знаешь, что такое рекурсия и где ее целесообразно применять :)

В подтверждение моих слов привожу функцию, которая решает поставленную тобой задачу с помощью рекурсивного регулярного выражения (хотя я не уверен, что мое решение оптимально).
PHP:
$str = '<div style="display:none"> 
....текст1
<div>
.......текст2
</div>
.....текст3
</div>

.... текст4

<div>
....текст5
</div>

<clInfo border=1/>
';

$tmp = parse_first_tag($str);
print_r($tmp);


/**
    функция обрабатывает правильно вложеные теги.
    Возвращет результирующий массив, состоящий из элементов
    со следующими параметрами:
        - tag => название тэга
        - attributes => свойства тэга
        - inner_text => внутренний текст
*/
function parse_first_tag($str)
{
    $a = array(); // тут формируется результирующий массив из элементов $element
    $element = array(
        'tag' => '',
        'attributes' => '',
        'inner_text' => '',
    );
    // составляем регулярное выражение
    $regexp = '{';
    // соответствует одинарному элементу (в конце тэга '/>')
    $regexp .= '<([a-z]+)(\\s[^>]*?|)/>|';
    // соответствует нормальному элементу с открывающим и закрывающим тэгом
    $regexp .= '<([a-z]+)(\\s.*?|)>((?:(?:(?!<\\3[\\s>/])(?!</\\3[\\s>]).)+|(?R))*)</\\3>';
    $regexp .= '}is';
    if (preg_match_all($regexp, $str, $matches)) {
        $n = sizeof($matches[1]); // количество совпадений
        for ($i = 0; $i < $n; $i++) {
            if (strlen($matches[1][$i]) > 0) {
                // регулярка совпала с одинарным элементом
                $element['tag'] = $matches[1][$i];
                $element['attributes'] = $matches[2][$i];
                $element['inner_text'] = '';
            } else {
                // регулярка совпала с нормальным элементом
                $element['tag'] = $matches[3][$i];
                $element['attributes'] = $matches[4][$i];
                $element['inner_text'] = $matches[5][$i];
            }
            array_push($a, $element);
        }
    }
    return $a;
}
 

Stur

Guest
Спасибо за пример, все работает нормально.
 
Сверху