Форум программистов, компьютерный форум, киберфорум
PHP: RegExp
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.89/27: Рейтинг темы: голосов - 27, средняя оценка - 4.89
0 / 0 / 0
Регистрация: 15.11.2010
Сообщений: 55

Повторение шаблона 0 и более раз

29.08.2013, 22:01. Показов 5366. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Вроде вопрос простой, но никак разобраться не могу.

Задача следующая. Нужно правильно взять то, что находится между тегами <tr></tr>, учитывая, что внутри этих тегов тоже могут быть таблицы. Если там есть таблицы, то регулярка не должна по ошибке взять закрывающийся тег </tr> из вложенной таблицы. Для случая когда есть одна вложенная таблица такая регулярка работает:

PHP
1
    preg_match_all('`<tr[^>]*>.*(?:.*<table[^>]*>.*</table[^>]*>.*)</tr[^>]*>`Uis',$string,$out);
А вот когда две вложенных таблицы - нет. Как к шаблону (?:.*<table[^>]*>.*</table[^>]*>.*) добавить "вхождение нуль или более раз"? Просто добавление звездочки после скобки ведет к ошибке.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
29.08.2013, 22:01
Ответы с готовыми решениями:

Найти вероятность того, что герб выпадет: 2 раза; не менее 2 раз; не более 2 раз; не менее одного и не более трех раз
Монету бросают 6 раз. Найти вероятность того, что герб выпадет: а) 2 раза; б) не менее 2 раз; в)не более 2 раз; г) не менее одного и не...

повторение n раз i:=i+2
Изучаю паскаль пару недель. Прошу проверить мою программу и дополнить, где неправильно. Задача: Есть массив из 6 элементов...

Повторение кода до 10 раз
#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;windows.h&gt; int main() { int(i); {do { printf( &quot;Hi!\n &quot;); ...

8
601 / 468 / 73
Регистрация: 22.01.2009
Сообщений: 1,180
Записей в блоге: 1
30.08.2013, 04:18
html считается валидным? все открывающие теги -- закрыты?

Добавлено через 28 минут
попробуйте вот так:
PHP
1
preg_match_all('`<tr[^>]*+>(?:[^<]*+(?(?!</?tr>)<|(?R)?))*+</tr>`S');
Добавлено через 2 минуты
Цитата Сообщение от ListOFF Посмотреть сообщение
Просто добавление звездочки после скобки ведет к ошибке.
не к ошибке, оно просто не заматчится, потому что у вас ungreedy. поставьте *?, если хотите. но на самом деле без рекурсии вы наврядли сможете обойтись, если заранее точно неизвестно какой уровень вложенности может быть
1
0 / 0 / 0
Регистрация: 15.11.2010
Сообщений: 55
30.08.2013, 11:17  [ТС]
NEbO, спасибо большое! Код должен быть валидным. Ваш вариант работает, но хотелось бы разобраться.
Не могли бы пояснить вот эту часть регулярки:

PHP
1
[^<]*+(?(?!</?tr>)<|(?R)?)
В этом шаблоне сначала могут идти любые символы кроме открывающего тега. А дальше? Что означает в данном контексте плюс? И с вопросами я тоже не понял их значения в выделенном куске (?! должен означать отрицание того, что за ним идет закрывающий тег </tr>). И конструкцию (?R) впервые вижу.
0
601 / 468 / 73
Регистрация: 22.01.2009
Сообщений: 1,180
Записей в блоге: 1
30.08.2013, 12:30
так сразу и не объясню) посмотрите условные подмаски, рекурсию, упреждающие проверки -- они все тут применяются. я пытался написать и другие виды регулярок, для парсинга html, где-то может есть и попроще, тут в соседних темах, в этом разделе, посмотрите, коли есть желание. пока вроде эта самая короткая, и должна быть достаточно производительной (по крайней мере не упало, на странице с этого форума, ну и отработало быстренько, вроде как).
вот помоему очень неплохую памятку по всему этому делу написал Vovan-VE, Памятка по регулярным выражениям PCRE в PHP недавно глянул, оказалось, там даже лучше написано, чем в офф манах пхп. ну на мой взгляд

Не по теме:

кстати, да, хоть мне лично и не пригодилось, но надо бы ему плюсануть:) отличная работа



Добавлено через 1 минуту
если будет совсем туго, мб что-то объясню, но позже. просто времени сейчас нет, извините. пока прочтите это, и посмотрите знакомые символы

Добавлено через 1 час 5 минут
так вот. по сути, я рассуждал так: после того, как нам встретился символ "<", нужно проверить, не идет ли впереди последовательности <tr> или </tr>, остальные нас не интересуют.

Не по теме:

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

. так вот, дойдя до состояния ".<" (т.е. мы находимся в позиции до "<"), проверяем последующие символы. это может быть <tr>, либо </tr>, и это дело заключено в негативной проверке "(?!</?tr>)"

Не по теме:

хотя ее можно преобразовать в позитивную, просто нужно поменять местами будет условную конструкцию

. так как проверка негативная, то первым исходом будет false. значит мы просто берем и пропускаем этот символ "<", и нам грубо говоря, чихать что там после него идет
Code
1
(?(?!</?tr>)<)

Не по теме:

кстати, поэтому я и применил негативную проверку, чтобы наиболее часто встречающаяся последовательность оказалась раньше. По крайней мере в конструкции альтернативного выбора, это работает быстрее. И вообще, в pcre допускается писать например, так: "a|b|c|." точка в этом случае совпадет со всеми символами, кроме a,b, и с. Помоему очевидно, что где-то внутри есть некая проверка, вернее в автомате, скорее всего стоит обычный switch, который берет первое попавшее совпадение, начиная слева. Хотя это не совсем документированная особенность, и по идее, не для всех движков регулярок применимо -- думаю, что некоторые могут оптимизировать как посчитают нужным, подобные конструкции, однако конкретно pcre проверяет последовательно, слева направо.


ну а если все таки дальше стоит "</?tr>" (т.е. либо <tr>, либо </tr>), то значит, мы можем войти в рекурсию

Не по теме:

начать проверять все выражение сначала, только запомнить, что надо вернуться обратно в это же место. Рекурсию я сам очень долго понимал, просто пришло, наверное, с опытом, пока писал регулярки тут на форуме:) впрочем, у меня самого временами возникают вопросы, а правильно ли я ее применяю, вероятно есть какое то более эффективное решение...

в рекурсию войти пробуем, но если все же дальше находится именно </tr>, а не <tr>, то нам нужно выйти из регулярки и закончить весь этот балаган. поэтому я поставил тупо "(?R)?+", т.е. рекурсия может быть, а может и не быть. и если ее нет, то мы просто выходим из всего этого дела, хотя вначале попытаемся пройтись еще раз по всей этой "большой" подмаске --
Code
1
(?:[^<]*+(?(?!</?tr>)<|(?R)?))
но совпадения не обнаружим, а значит, выйдем отсюда, и перейдем к последней части -- "</tr>", и завершим уровень рекурсии. Далее таким же образом завершаются все предыдущие уровни, ну и после этого происходит выход из самой регулярки.
Надеюсь, что хоть чуть-чуть ясности я все же внес. Знаю, что объяснятор из меня так себе прочтите книжку Фридла, он получше объясняет, с картинками)) впрочем, мне лично его объяснений рекурсии почему-то не хватило, все равно еще долго потом с ней разбирался, и до сих пор все тестирую что пишу. иногда (тока тсс!) наугад ставлю знаки вопроса и смотрю, что сработало, а что нет потом придумываю объяснение происходящему
вот. ну а вообще, в регулярке точно есть несколько недочетов. по крайней мере, щас не должно сработать с вложенными аттрибутами. довольно просто это преодолеть вот так:
Code
1
<tr[^>]*+>(?:[^<]*+(?(?!</?tr\b)<|(?R)?))*+</tr>
так просто проверка будет работать не на точный тег "</?tr>", а на любую последовательность "</?tr", после которой идет что-то разделяющее слова (оператор, или пробел, ну короче не буква не цифра и не что-то в этом роде), т.е. по сути идет как раз таки тег "<tr>" или закрывающий "</tr>", вероятно, с атрибутами, вероятно, без.
Вообщем, как-то так

Не по теме:

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

1
0 / 0 / 0
Регистрация: 15.11.2010
Сообщений: 55
31.08.2013, 18:24  [ТС]
NEbO, еще раз огромное спасибо! Сейчас поразбираюсь с рекурсиями в регулярках - возможно, еще вопросы появятся. Единственное не понял про замену класса [^>] на . Ведь во втором случае в выражении <tr.*> можно выйти за пределы тега

Добавлено через 22 часа 48 минут
NEbO, появились вопросы по вашей регулярке.

1.Вот я дошел до ситуации, когда появилась комбинация <table>, допустим. Вот я смотрю на это условие:

Code
1
(?(?!</?tr>)<|(?R)?))
Получается, в этом случае ищется шаблон "<" без входа в рекурсию. Предположим, у меня такая конструкция:

Code
1
<tr><td><table>....</table></td></tr>
Я дохожу до <td>, это не <tr>, значит дальше ищу <. Нахожу его у <table> и что дальше? Каким образом проверка пойдет далее, вот этот механизм я не понимаю

2.
Цитата Сообщение от NEbO Посмотреть сообщение
я поставил тупо "(?R)?+", т.е. рекурсия может быть, а может и не быть.
Так все-таки "(?R)?+" или "(?R)?" В первоначальном варианте плюса не было.

3. И все-таки, если можно, поясните, почему не работает такая регулярка:

PHP
1
preg_match_all('`<td[^>]*>(.*?(?:<table[^>]*>.*?</table[^>]*>)*.*?)</td[^>]*>`i',$v,$td);
Тут то же самое, только теперь надо правильно вычленить все что между <td></td>, учитывая, что там может таблица, которая конечно будет содержать тоже в свою очередь <td>. Идея такой регулярки простая: если встречается <table>, то его одержимое пропускать как единое целое. Т.е. есть комбинация (<table[^>]*>.*?</table[^>]*>), которая может быть, а может и не быть (нуль или более повторений). Но этот вариант почему-то не работает.
0
601 / 468 / 73
Регистрация: 22.01.2009
Сообщений: 1,180
Записей в блоге: 1
31.08.2013, 19:56
Цитата Сообщение от ListOFF Посмотреть сообщение
Но этот вариант почему-то не работает.
потому что могут быть вложенные table. Регулярка относительно "тупая": она просто берет и идет до первого (изза нежадности) </table>, и все. если там будет несколько вложенных </table>, то грубо говоря, вам нужно считать количество открытых и закрытых, иначе вы эту проблему не решите.
Наглядно: возьмите например, просто запишите в одну строчку сложный html документ. И попробуйте сами вручную выделить те части, которые вам нужны. без учета вложенности это сделать относительно просто. А вот со вложенностью, вам придется очень сильно помучаться.
Другой пример, уже более приближенный к реальности, и конечным автоматам вообще: решите задачу о проверке последовательности скобок на "правильность". как обычно, правильная скобочная последовательность задается правилами: "" -- правильная скобочная последовательность, и для любой скобочной последовательности A, справедливо, что "()A" и "(A)" -- также правильные скобочные последовательности.
когда решите эту задачу, поймете, что без рекурсии, в общем случае, вам не обойтись (если неизвестен хотябы максимальный уровень вложенности, однако даже если известен и это не 1 и не 2, то автомат, как и регулярка, будут очень большими).
кроме того, ваша регулярка будет выполняться с экспоненциальной сложностью. причем с настолько жутко экспоненциальной, что я очень сомневаюсь что оно отработает на 100-200кб странице, пусть даже и без глубокой вложенности, но с достаточно большим количеством тегов <td> и вложенной в него <table>.
Цитата Сообщение от ListOFF Посмотреть сообщение
Так все-таки "(?R)?+" или "(?R)?" В первоначальном варианте плюса не было.
в данном случае не суть. если + стоит после квантификатора, это означает что квантификатор будет работать в "сверхжадном режиме". о котором, я кажется уже говорил. вообщем, если что-то там совпало, то автомат не будет откатываться назад и перепроверять. подробнее -- посмотрите на вики, или у фридла, чтобы по настоящему понимать зачем это нужно, нужно знать как работает движок регулярок. если вам все это не надо, не обращайте внимания, просто если напишите +, то в среднем это может сэкономить несколько поверок и откатов, но вообще говоря, не так уж много, потому что внешняя скобка уже работает в "сверхжадном режиме", т.е. по сути откаты будут только в очень неболоьшом количестве случаев.
Цитата Сообщение от ListOFF Посмотреть сообщение
Я дохожу до <td>, это не <tr>, значит дальше ищу <. Нахожу его у <table> и что дальше? Каким образом проверка пойдет далее, вот этот механизм я не понимаю
Цитата Сообщение от ListOFF Посмотреть сообщение
Code
1
(?(?!</?tr>)<|(?R)?))
еще раз, "если дальше не идет </?tr>, то захватываем угловую скобку, и идем дальше. если же идет, то пробуем войти в рекурсию. после этого возвращаемся в состояние (?:[^<]*+(?(?!</?tr\b)<|(?R)?))*+ и опять идем до первой открывающей угловой скобки, и так далее"

Добавлено через 7 минут
Цитата Сообщение от ListOFF Посмотреть сообщение
не понял про замену класса [^>] на .
а я не понял, где вы это увидели я просто добавил во вложенную упреждающую проверку (?=</?tr\b), вместо фиксированной </?tr>, чтобы учитывать что после tr может идти пробел, например, а не только сразу угловая скобка
0
0 / 0 / 0
Регистрация: 15.11.2010
Сообщений: 55
31.08.2013, 21:24  [ТС]
Цитата Сообщение от NEbO Посмотреть сообщение
а я не понял, где вы это увидели
Цитата Сообщение от NEbO Посмотреть сообщение
а класс [^<], вообще говоря, необязателен, можно было бы и просто точку поставить
Вот это я имел в виду.

Но важно сейчас другое. Регулярка правильно выдернула строки из таблицы. Получился массив, каждый элемент которого - строка таблицы. Ту же самую задачу сейчас надо решить, только для тега <td></td>. Собственно между этими тегами и лежит вложенная таблица. Казалось бы, регулярное выражение точно такое же, нужно только заменить в нем <tr> на <td>. Но это срабатывает только когда таблица в <td> одна. Когда их несколько, т.е.

Code
1
<tr><td>....<table>.....</table>....<table>....</table>....</td></tr>
preg_match_all выдает какое-то мясо, все ячейки парсятся как отедельные, а дальше со следующей строки вообще странности начинаются. Страницу проверил на валидность. Есть конечно ошибки, но <td> это не задевает
0
601 / 468 / 73
Регистрация: 22.01.2009
Сообщений: 1,180
Записей в блоге: 1
31.08.2013, 21:50
Цитата Сообщение от ListOFF Посмотреть сообщение
Вот это я имел в виду.
я а имел ввиду, что можно так:
Code
1
<tr[^>]*+>(?:.*?(?(?!</?tr\b)<|(?R)?))*+</tr>
или так:
Code
1
<tr[^>]*+>(?:.(?(?!</?tr\b)<|(?R)?))*+</tr>
но лучше так не делать.

по поводу второго вопроса -- покажите конкретные данные, на которых не срабатывает.
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$data = <<<DATA
<tr><td>....
<table>.....
</table>....
 
<table>....
<tr><td></td></tr>
</table>....
 
</td>
 
<td>
asasasas
</td></tr>
DATA;
 
preg_match_all('~<td[^>]*+>(?:[^<]*+(?(?!</?td\b)<|(?R)?))*+</td>~i', $data, $matches);
print_r($matches);
у меня показывает то, что должно быть.

Не по теме:

похоже вы так собираетесь всю хтмл-ную грамматику распарсить... применяйте лучше DOMDocument, для этих целей, или любой другой xml парсер, их в пхп только встроенных 4 штуки, если мне память не изменяет. Регулярки, если вы их применяете именно в таком виде, будут обходить и парсить одно и то же по многу раз, это неэффективно. На то они и регулярные грамматики, что КС ими по нормальному не распарсить

0
0 / 0 / 0
Регистрация: 15.11.2010
Сообщений: 55
31.08.2013, 21:55  [ТС]
Цитата Сообщение от NEbO Посмотреть сообщение
похоже вы так собираетесь всю хтмл-ную грамматику распарсить
Нет, тут надо конкретные таблицы распарсить и всё.
Цитата Сообщение от NEbO Посмотреть сообщение
покажите конкретные данные, на которых не срабатывает
Я это лучше в личку отправлю, чтобы тут тему не загромождать
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
31.08.2013, 21:55
Помогаю со студенческими работами здесь

Повторение музыки 5 раз js
Всем привет! Кто поможет, нужно чтобы музыка повторялась 5 раз, а после 5 раза перестала играть, часть кода отвечающая за музыку: ...

Повторение определенной буквы в строке заданное количество раз
Доброго времени суток!Есть такой вопрос: Я ввожу из консоли строку такого типа: abc(3), и мне надо сделать так, чтобы моя программа...

Повторение слова заданное количество раз: исправить код
написала макрос для своей лабы.условие состоит в том что в ком.строке задается 2 параметра 1 слово 2-число повторений пужно записать это...

Найти вероятность того, что событие появиться 90 раз и более 90 раз.
вероятность появления событий в каждом из 100 независимых испытаний постоянна и ровна. найти вероятность того что событие появиться : а)90...

Найти вероятность того, что в этих испытаниях событие А появится не менее k1 раз и не более k2 раз
Дана вероятность р появления события А в каждом из п независимых испытаний. Найти вероятность того, что в этих испытаниях событие А...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru