| 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 | 
                     Похожие запросы: 
  | 
    
         |