nixp.ru v3.0

29 мая 2017,
понедельник,
18:11:11 MSK

DevOps с компанией «Флант»
_den_ написал 21 июня 2007 года в 13:30 (505 просмотров) Ведет себя неопределенно; открыл 4 темы в форуме, оставил 8 комментариев на сайте.

Какзалось бы тривиальная задача — получить список открывающих HTML тэгов через регулярное выражение в preg_match_all(php) .

Но никак не могу составить шаблон, который бы исключал тэги вида . Т.е. не включать тэги, если перед закрывающей скобкой стоит «/».

Можь кто поделиться шаблончиком, если у кого завалялся? Буду очень и очень призщателен.

Дмитрий Шурупов

А добавить перед этой закрывающей «>» какое-нибудь »[^\/]«?

Дмитрий Шурупов

Вот неизящный Perl-овый пример (regexp’ы-то едины…):

my $html = qq~


<html>
<body>
text

tex</b>t</h2></div></body></html>~;

print «html is: „.$html.“\n»;

my $tags;

while ($html =~ /<\w>|<[^\/][^>]*[^\/]>/){

$html = $`.$’;

$tags .= $&;

}

print «tags are: „.$tags.“\n»;

Выполнение:

[b]$ perl a.pl
html is: [/b]


<body>
text

te<strong>x</b>t</h2></div></body></html>

</strong>

tags are:

</body>

</body>
</html>

_den_

Да в том-то и дело, что в переметрах самого тэга символ «/» может встречаться, скажем URL какой-нить, то есть срабатывать должно только если «/» стоит непосредственно перед >.

Дмитрий Шурупов

Так и поставь непосредственно перед > — только для одного символа! См. пример выше.

Там появляется «<\w>|», потому что иначе не будут учитываться теги типа »» (рассматриваются, минимум 2 символа, т.к. проверка на «без /» в начале и «без /» в конце).

p.s.

[b]$ perl a.pl
html is: [/b]


<body>
tex</a>t

te<strong>x</b>t</h2></div></body></html>

</strong>

tags are:

</body>

_den_

В принципе ясно, из этого можно получить нужный список, еще раз прогнав, и выдрав уже непосредственно имя тэгов.

Я пытался сделать это в одном выражении. Т.е получить список вида  html,body,div,h2… (из твоего примера).

Вообще возможен такой вариант?

Спасибо за участие :).

Дмитрий Шурупов

Да легко:

my $html = qq~


<html>
<body>
tex</a>t

tex</b>t</h2></div></body></html>~;

print «html is: „.$html.“\n»;

my $tags;

while ($html =~ /<([^\/][^\s>]*)[^>]*(?<!(\/))>/){

$tags .= $1.«,»;

$html = $`.$’;

}

print «tags are: „.$tags.“\n»;


$ perl a.pl
html is: 


<html>
<body>
tex</a>t

tex</b>t</h2></div></body></html>

tags are: html,body,div,a,h2,b,

</body>
</html>
</body>
</html>

_den_

Блин крут… :), в принципе делает почти то-что нужно только вот не пойму почему не срабатывает второе условие на закрывающие тэги :

preg_match_all(’@<([^/][^\s>])[^>]*(?|</(\w+)[^>]>@’,$html,$tags) ;

Т.е. пытаюс получить спискок закрывающих и открывающих тэгов в порядке их следования, ну понятное дело чтобы провести простую проверку на корректность вложенности и т.д.

Дмитрий Шурупов

«</(\w+)[^>]>» — не очень корректно. Зачем тут и «\w+», и »[^>]«? Не будет работать для тега из одной буквы.

Можно и «</(\w+)>», и «</([^>]+)>». Но когда все вместе — получается не совсем то.

_den_

</(\w+)[^>]*> квалификатор '*' скушало почему-то.

В принципе может и не совсем корректно, но отрабатывает так как нужно, если не в составе вышеприведенного шаблона.

Дмитрий Шурупов

Да, тут <звездочка>текст<звездочка> заменяют на bold для «текст». Поэтому лучше такие вещи помещать в теги [code ]текст[/code ] (в обоих случаях, естественно, надо без пробелов между «code» и «]»).

А все равно: зачем »[^>]*» тут вообще? :-) Такой HTML не будет валидным…

В твоем случае, полагаю, не выцепляет закрывающие, потому что они хранятся уже не в $matches[0], как в случае с открывающим (regexp-то тут для всего один). Я бы сделал так:

my $html = qq~


<html>
<body>
tex</a>t

tex</b>t</h2></div></body></html>~;

print «html is: „.$html.“\n»;

my ($open_tags, $close_tags);

while ($html =~ /<([^\/][^\s>]*)[^>]*(?<!(\/))>/){

$open_tags .= $1.«,»;

$html = $`.$’;

}

while ($html =~ /<\/(\w+)>/){

$close_tags .= $1.«,»;

$html = $`.$’;

}

print «open tags are: „.$open_tags.“\n»;

print «close tags are: „.$close_tags.“\n»;

Получаем:

$ perl a.pl
html is: 


<html>
<body>
tex</a>t

tex</b>t</h2></div></body></html>

open tags are: html,body,div,a,h2,b,

close tags are: a,b,h2,div,body,html,

Логичнее все это кидать по массивам и тогда уже проверять. Ну, или на лету. По вкусу :-)
</body>
</html>
</body>
</html>

_den_

Да они в принципе по идее и так будут в двух массивах.

примерно:

tags[1] = array (’tag1\′,’tag2\′,»,’tag4\′);

tags[2] = array (»,»,’tag3\′,»);

В твоем примере мы теряем общую последовательность тегов, т.е. как узнать за каким открывающим идет какой закрывающи тэг. По этой причине я и пытаюсь загнать  в единное выражение. Т.е. кроме наличия парных тегов хочется проверять и правльность их вложенности.

Дмитрий Шурупов

Вот такая штука в PHP:

preg_match_all('@<([^/][^\s>]*)[^>]*(?<!(/))>|</(\w+[^>]*)>@',$html,$tags);

Вернула мне:

Array
(
    [0] => Array
        (
            [0] => 


<html>
<body>

[1] =>

[2] =>

[3] =>

[4] => </a>

[5] =>

[6] =>

[7] => </b>

[8] => </h2>

[9] => </div>

[10] => </body>

[11] => </html>

)

[1] => Array

(

[0] => html

[1] => body

[2] => div

[3] => a

[4] =>

[5] => h2

[6] => b

[7] =>

[8] =>

[9] =>

[10] =>

[11] =>

)

[3] => Array

(

[0] =>

[1] =>

[2] =>

[3] => a

[4] =>

[5] => b

[6] => h2

[7] => div

[8] => body

[9] => html

)

М?..
</body>
</html>

_den_

Да оно самое! С небольшими изменениями:

preg_match_all('@<([^/][^\s>]*)[^>]*(?<!(?:/))>|</(\w+)[^>]*>@',$html,$tags);

получилось как-раз то-что ожидал.

Просто ОГРОМНОЕ спасибо Dmitry.

К стати пока так и не понял как оно работает :), и покурю на досуге Фридла, что-ли…