01 | На заметку: |
Здесь и в других темах, касающихся использования регулярных выражений, очень сильно помогают Regex-помощники — простые программы по тестированию регулярных выражений. О такой программе, написанной автором, можно почитать в статье Codius RegexTester v1.0 - тестирование регулярных выражений. Также там её можно скачать и пользоваться. Абсолютно бесплатно.
|
|
02 |
Эта статья, обязана своим появлением, возникшей однажды необходимости распарсить HTML. Столкнулся я с этим при работе над статьей Пишем свой Code Highlighter (раскрашиваем код). После недолгих поисков было найдено регулярное выражение, с которым можно было работать — <div>(?><div>(?<DEPTH>)|</div>(?<-DEPTH>)|.?)*(?(DEPTH)(?!))</div>. Рассмотрим детально, как оно работает: |
|
03 | RegExp |
1 2 3 4 5 6 7 8 9 10 <div>
(?>
<div> (?<DEPTH>)
|
</div> (?<-DEPTH>)
|
.?
)*
(?(DEPTH)(?!))
</div> |
|
04 |
|
|
05 | На заметку: |
Нельзя забывать об установке необходимых параметров: Singleline (s) — для игнорирования знаков переноса строки, IgnoreCase (i) — для игнорирования регистра и IgnorePatternWhitespace — для игнорирования пробелов.
|
|
06 |
Если попытаться очень упрощенно объяснить, что происходит в данном выражении, то получится примерно следующее: в третьей строке при нахождении вложенного тега — параметр глубины увеличивается на 1 (помещение значения в стек), при нахождении закрывающего тега — параметр глубины уменьшается на 1 (удаление последнего добавленного значения из стека), и в итоге происходит проверка — если количество открытых тегов равно количеству закрытых тегов (т. е. стек с именем DEPTH пуст/отсутствует), то вернуть найденное выражение. Пример: |
|
07 | HTML |
1 2 3 4 5 6 7 <Content 1</ >
< >
Content 2
< >Content 3</ >
</ >
</ > >
< > |
|
08 |
Но при использовании данного выражения всплывает один неприятный момент — если закрывающих тегов будет больше, то это выражение «зацепит» и их, т. е. в ситуации: |
|
09 | HTML |
1 2 3 4 5 6 7 8 9 <Content 1</ >
< >
Content 2
< >Content 3</ >
</ >
>
< > </
</ > >
</ > |
|
10 |
будет захвачена вся строка для поиска (включая лишние выделенные строки). |
|
11 |
Обратимся к MSDN — Конструкции группировки в регулярных выражениях раздел «Сбалансированные определения групп». Там рассматривается подобный вариант работы с вложенными конструкциями. Изменим наше выражение в соответствии с материалом из MSDN (поскольку конструкция меняется, то для удобства именования групп будем использовать синтаксис с одинарными кавычками, а не угловыми скобками) — |
|
12 | RegExp |
1 2 3 4 5 6 7 8 9 10 11 12 (
(
(?'Open'<div>)
.*?
(?=(</?div>))
)+
(
(?'Close-Open'</div>)
.*?
)+
)+
(?(Open)(?!)) |
|
13 |
|
|
14 |
Это выражение будет работать уже корректно — будет захвачено только то, что нужно: |
|
15 | HTML |
1 2 3 4 5 6 7 8 9 <
> <Content 1</ >
> <
> Content 2
<Content 3</ >
> </
> </
</ > >
</ > |
|
16 |
Доработаем данное регулярное выражение таким образом, чтобы оно искало не только теги <div>, а и любые другие теги, а также чтобы оно нормально работало с тегами, у которых указаны какие-либо атрибуты — <div class="content">. |
|
17 |
Для поиска любых тегов внесем следующие изменения: |
|
18 | RegExp |
1 2 3 4 5 6 7 8 9 10 11 12 (
(
(?'Open'<b><(?'tag'[\w-]+)</b>)
.*?
(?=(</?<b>\k'tag'</b>>))
)+
(
(?'Close-Open'</<b>\k'tag'</b>>)
.*?
)+
)+
(?(Open)(?!)) |
|
19 |
|
|
20 |
Теперь добавим фрагмент, который позволит не обращать внимание на атрибуты тегов: |
|
21 | RegExp |
1 2 3 4 5 6 7 8 9 10 11 12 (
(
(?'Open'<(?'tag'[\w-]+)(\s+[\w-]+="[^"]*")*)
.*?
(?=(</?\k'tag'>)) #Удаляем символ '>' - он нам не нужен
)+
(
(?'Close-Open'</\k'tag'>)
.*?
)+
)+
(?(Open)(?!)) |
|
22 |
|
|
23 |
Итоговое выражение — (((?'Open'<(?'tag'[\w-]+)(\s+[\w-]+="[^"]*")*).*?(?=(</?\k'tag')))+((?'Close-Open'</\k'tag'>).*?)+)+(?(Open)(?!)). |
|
24 | RegExp |
1 2 3 4 5 6 7 8 9 10 11 12 (
(
(?'Open'<(?'tag'[\w-]+)(\s+[\w-]+="[^"]*")*)
.*?
(?=(</?\k'tag'))
)+
(
(?'Close-Open'</\k'tag'>)
.*?
)+
)+
(?(Open)(?!)) |
|
26 |
Похожие запросы:
|
|