Что нового

Аналог outerHTML через рекурсию (субрутины)

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Как достать текст также, как это делает функция outerHTML при разборе HTML, но уже регулярными выражениями?

Вот к примеру, есть много вложенных друг в друга div, нужно достать в массив outerHTML для <div class="listEntry">
Код:
<div class="listEntry">
	<div>1</div>
	<div></div>
	<div><div></div></div>
</div>

<div class="listEntry">
	<div>2</div>
	<div></div>
	<div><div></div></div>
</div>
Думал можно через субрутины, но не выходит.
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
Re: Аналог outerHTML

если число вложеных известно и одинаково, то
Код:
#include 'array.au3'
$file=FileRead(@ScriptDir&'\111')
$str=StringRegExp($file,'(?s)<div class="listEntry">.*?<div>([^<]*).*?<div>([^<]*).*?<div>([^<]*)',3)
_ArrayDisplay($str)

в иных случаях нельзя
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

Не известно сколько вложенных
 

DarWiM

Продвинутый
Сообщения
527
Репутация
90
Re: Аналог outerHTML

inververs
Тогда без рекурсий в выражении не обойтись :(
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

DarWiM сказал(а):
inververs
Тогда без рекурсий в выражении не обойтись :(
Да, но как ее правильно вписать.. рекурсии можно добавлять через (?номер) - в 3.3.12.0 добавили возможность.
Только я не могу понять как их использовать для этого случая..


Добавлено:
Сообщение автоматически объединено:

Гиблое дело думаю..
 

DarWiM

Продвинутый
Сообщения
527
Репутация
90
Re: Аналог outerHTML

inververs
Можно полюбопытствовать, зачем такие извращения нужны?
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

DarWiM
Нужно распарсить html страницы. И нужно быстро, страниц много.
Пробую через ObjCreate('HTMLFILE'), но только на то что бы загрузить станицу в объект, уже тратится целая секунда.
И поэтому хочу попробовать через регулярные варажения, может будет быстрее.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Re: Аналог outerHTML

Давно собирался разобраться с рекурсией, да всё руки не доходили, а тут inververs...
Ну, у меня получилось вот так.

Код:
#include 'array.au3'

$file=FileRead('1111.txt')

$pattern = '(?s)(?(DEFINE)(?<tag><div(?&tag)*?</div>|.))<div class="listEntry">(?&tag)*?</div>'

$str=StringRegExp($file, $pattern, 3)
_ArrayDisplay($str)


Я крут! :laugh:
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

Ух ты, здорово! Проверил на реальной странице, все заработало!
C2H5OH, да, ты крут! А я вот не смог понять. (даже с этим примером :smile: )
Самое интересное, это первый пример регулярки с рекурсией.
Спасибо огромное.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Re: Аналог outerHTML

А я вот не смог понять.

Ты думаешь я два дня чем занимался?
В справке одни намёки отдалённые...
Вот сегодня наконец допёр что надо для рекурсии в самой субрутине не (?R) ставить, а (?&имя)

А с этим примером то что не понятно?


Добавлено:
Сообщение автоматически объединено:

Кстати, а по времени как? Регулярка даёт ощутимый выигрыш?
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

C2H5OH [?]
А с этим примером то что не понятно?
Не понятно как это работает (?(DEFINE)(?<tag><div(?&tag)*?</div>|.)



Добавлено:
Сообщение автоматически объединено:

C2H5OH [?]
Кстати, а по времени как? Регулярка даёт ощутимый выигрыш?
Еще как!
Если загружать в объект, то нужно подождать пока документ полностью не запишется, а это у меня занимает около 1 секунды.
Потом мне нужно найти $document.getElementsByClassName('listEntry'), получить коллекцию, по которой нужно пройтись, что бы доставать необходимую информацию.
А с регуляркой, я сразу получаю массив, и всего за 12 миллисекунд.
Выигрыш огромный!
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Re: Аналог outerHTML

Сначала алгоритм.
Вот мы нашли в тексте имя тега <div (вторая скобка '>' не написана специально, сами догадайтесь почему)
Что может встретиться внутри этого тега между <div и </div> ?
Либо ещё такой же тег <div - </div> , либо другой совершенно произвольный символ.
Если встретилось начало такого же тега <div , то мы, естественно, делаем рекурсивный вызов шаблона, потому что перед нами встаёт такой же вопрос "а что может быть внутри?".

Теперь синтаксис.

Код:
$pattern = '(?s)(?(DEFINE)(?<tag><div(?&tag)*?</div>|.))<div class="listEntry">(?&tag)*?</div>'


(?s) - это понятно

(?(DEFINE) - сейчас будет описание субрутины

(?<tag> - субрутина будет называться 'tag'

<div(?&tag)*?</div>|. - сам шаблон субрутины с рекурсивным вызовом: либо вложенный тег <div - </div>, либо какой-то символ.
Где (?&tag) - собственно рекурсивный вызов субрутиной самой себя.

<div class="listEntry">(?&tag)*?</div> - основной паттерн, который делает первое обращение к рекурсивной субрутине.

Так понятно?
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

<div(?&tag)*?</div>|.
Вот эта часть. Это тело субрутины? Как оно ищет:
Ищем <div затем пытаемся вызввать субрутину (<div?) т.е <div<div<div<div итп?, а если нет, то любой символ, скажем <div . а тут опять пробуем найти <div рекурсивно.

Все остальное понятно.



Добавлено:
Сообщение автоматически объединено:

Можно сломать мозг ;D
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Re: Аналог outerHTML

Между <div и СООТВЕТСТВУЮЩЕМУ ЕМУ </div> N раз встречается что-то, совпадающее с шаблоном субрутины.
Это что-то может быть одиночный символ '<div(?&tag)*?</div>|.' <- '.'
Либо такой же тег <div -- </div>.
Если это тег <div -- </div>, то внутри его M раз встречается что-то, совпадающее с шаблоном субрутины, поэтому мы внутри вызываем рекурсивно сам этот же шаблон
<div(?&tag)*?</div>
А сколько символов или вложенных тегов может быть внутри <div -- </div> ?
Вот столько
<div(?&tag)*?</div>
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

C2H5OH
А такой пример: {(?:[^{}]*+|(?0))*} , вам понятен? Что он делает?
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Re: Аналог outerHTML

Насколько я понимаю,
' {(?:[^{}]*+|(?0))*} '
делает то же самое что вот такой паттерн
'(?(DEFINE)(?<drink>[^{}]*+|(?&drink))){(?&drink)*}'

Потетстить бы, но уже в дверях одной ногой...


Добавлено:
Сообщение автоматически объединено:

Я бы и для div вместо { написал похожий паттерн, если бы подсказали как для <div и </div> написать аналог [^{}]
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Re: Аналог outerHTML

Я и сам не знаю, как делать НЕ для целой строки. Увы.
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
Re: Аналог outerHTML

а что надо получить в итоге?
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Re: Аналог outerHTML

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

Почему нам не подходит схема паттерна как в примере с круглыми скобками?
{(?:[^{}]*+|(?0))*}
Этот паттерн находит все рекурсивные блоки заключенные в {}.
Но скобки самого верхнего уровня равнозначны внутренним скобкам. И поэтому тут применим рекурсивный вызов всего паттерна (?0) или (?R).
Но у нас div верхнего уровня отличается, он содержит class="listEntry"
И если мы напишем аналогичный скобочному паттерн для div, примерно так
Код:
#include 'array.au3'

$file=FileRead('1111.txt')

$pattern = '(?s)<div(?:(?0)|.)*?</div>'
; или  $pattern = '(?s)<div(?:(?R)|.)*?</div>'

$str=StringRegExp($file, $pattern, 3)
_ArrayDisplay($str)


То такой паттерн вернёт нам нужные теги, но только потому, что в тестовых данных, приведённых ТС в самом начале, теги <div class="listEntry"> являются тегами верхнего уровня. Что в реальной ситуации маловероятно. Скорее всего исходные данные будут иметь вид
Код:
<div>
 <div class="listEntry">
	<div>1</div>
	<div>22</div>
	<div>222<div>ппп</div></div>
 </div>

 <div class="listEntry">
	<div>2</div>
	<div></div>
	<div><div></div></div>
 </div>
</div>

И результатом работы такого паттерна будет самый верхний тег div, то есть вся страница, которую мы хотим обработать.
Так что паттерн
Код:
$pattern = '(?s)<div(?:(?0)|.)*?</div>'


не смотря на краткость и изящность, к сожалению, не решает нашу задачу. :(

Но что интересно в примере с круглыми скобками? (ну, для меня лично, по меньшей мере)
Вызов субрутины по номеру (?0)
нулевая субрутина - это весь паттерн.
Как мы можем это использовать? Отказаться от описания имени в (DEFINE) и делать рекурсивный вызов по номеру.
Вот так
Код:
#include 'array.au3'

$file=FileRead('1111.txt')

$pattern = '(?s)(?(DEFINE)(<div(?1)*?</div>|.))<div class="listEntry">(?1)*?</div>'

$str=StringRegExp($file, $pattern, 3)
_ArrayDisplay($str)


(?(DEFINE) - сейчас будет описание субрутины (ПЕРВОЙ по порядку)
(<div(?1)*?</div>|.) - сам шаблон субрутины с рекурсивным вызовом субрутины первой по порядку во всём шаблоне (?1).

Ну, вроде бы шаблон по длинне меньше чем с именованной субрутиной, но скорость работы очевидно что одинаковая.
Облегчает ли понимание использование номеров субрутин вместо имён? Это уж кому как нравится. :smile:
 
Верх