Соответствующие вложенные теги - PullRequest
3 голосов
/ 06 января 2010

Pre-scriptum: Мне просто любопытно, и я знаю о других совершенно подходящих решениях, которые лежат вне области регулярных выражений.

Как сопоставить начальный тег и закрывающий тег с возможными вложенными и, возможно, идентичными тегами. Скажем, я дал в файле HTML:

<div class="nice">
    <a href="http://www.google.com">Hello</a>
    <div>World</div>
</div>

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

/(<div\sclass=\"nice\">(.*)</div>)/

Но это, конечно, будет соответствовать до тех пор, пока закрывающий тег div не будет ОЧЕНЬ ПОСЛЕДНИМ, что сделает код нечистым, если хороший div встроен в другой div. Отсутствие жадности в качестве разделителя сделает код еще более нечистым, пока он не будет соответствовать тегу VERY FIRST закрывающего div.

Так есть идеи? Я часто думал об этом, и я никогда не находил решения, это невозможно в регулярных выражениях, или только я забыл что-то простое? Есть ли какой-то механизм «оглядки назад»?

Ответы [ 7 ]

6 голосов
/ 06 января 2010

Это невозможно в регулярном выражении .

Вместо этого используйте HTML-анализатор, например Beautiful Soup , html5lib , hpricot или nokogiri

3 голосов
/ 06 января 2010

Сбалансированное сопоставление, кажется, является очень правильным инструментом для этого, и, вероятно, может быть реализовано на многих языках, но Perl и .NET делают лучшие попытки, насколько я вижу. Поскольку у Perl самый простой пример, вот один (заимствованный из http://www.perl.com/pub/a/2003/06/06/regexps.html):

$paren = qr/
      \(
        ( 
           [^()]+  # Not parens
         | 
           (??{ $paren })  # Another balanced group (not interpolated yet)
        )*
      \)
    /x;

(?? {$ paren}) просто ссылается на само регулярное выражение, приводящее к рекурсивному регулярному выражению. Прекрасно, я думаю, мне следовало упомянуть, что я был открыт для таких решений, но, конечно, это совсем не пример с регулярными выражениями, что, конечно, невозможно по определению:)

3 голосов
/ 06 января 2010

.NET-реализация Regex - одна из немногих, которая может справиться с этим сценарием. Он предлагает сбалансированное сопоставление , где можно использовать группы и считать их для анализа вложенных шаблонов.

Однако, это все еще не идеальное решение. Например, если вы добавите некорректный html-комментарий в микс, то даже умное регулярное выражение со сбалансированным соответствием может потерпеть неудачу. Так что все же лучше использовать html-парсер.

2 голосов
/ 14 февраля 2014

Не то чтобы я предлагал его использовать, но:

'#\<([\w]+)([^>]*?)(([\s]*\/>)|(\>((([^\<]*?|<\!\-\-.*?\-\->)|(?R))*)\<\/\1[\s]*\>))#sm'

должно работать, соответствует любому тегу, может быть настроено для соответствия определенному тегу.

2 голосов
/ 06 января 2010

Обычный совет не использовать для регулярных выражений для HTML, поскольку HTML не обычный. Так что попытки разобрать его с помощью регулярных выражений (особенно для выполнения чего-то строгого, как указано выше) будут сопряжены с трудностями.

1 голос
/ 20 марта 2018

Мое решение для регулярных выражений javascript (правильно обработанные вложенные теги)

Алгоритм:

  1. От всех совпадений регулярных выражений до открывающего тега мы берем последнее совпадение
  2. Временно удалить текст перед последним открывающим тегом и сам тег
  3. В оставшемся тексте мы ищем первый закрывающий тег и помечаем его как </tagnameGUID>

И повторите это для других матчей:)

Функция не анализирует самозакрывающиеся теги

function get_arr_tags(txt, tag) {
   function S4() {
      return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
   }
   function GUID() {
      return (S4() + S4()).toUpperCase();
   }
   var arr = [];
   if (!txt || !tag) return arr;
   var r_open = null;
   var r_close = null;
   var guid = GUID();
   r_open = RegExp('<' + tag + '\\b[^>\\/]*?>', 'gi');
   r_close = RegExp('<\\s*?\/\\s*?(' + tag + ')\\b[^>]*?>', 'gi');
   var m_arr = [];
   for (match = r_open.exec(txt); match != null; match = r_open.exec(txt)) {
      m_arr.push(match);
   }
   for (var i = m_arr.length - 1; i >= 0; i--) {
      var last_m_open = m_arr[i];
      r_close.lastIndex = 0;
      var frst_m_close = r_close.exec(txt.substring(last_m_open.index));
      var real_close_idx = last_m_open.index + frst_m_close.index;

      var obj = {
         'begin_idx': last_m_open.index,
         'open_tag': last_m_open[0],
         'close_tag': frst_m_close[0],
         'outerHTML': txt.substring(last_m_open.index, real_close_idx + frst_m_close[0].length).replace(RegExp(guid, 'g'), ''),
         'innerHTML': txt.substring(last_m_open.index + last_m_open[0].length, real_close_idx).replace(RegExp(guid, 'g'), '')
      }
      obj.close_tag_begin = obj.begin_idx + obj.open_tag.length + obj.innerHTML.length;
      obj.end_idx = obj.close_tag_begin + obj.close_tag.length;

      arr.splice(0, 0, obj);

      txt = txt.substring(0, real_close_idx) +
      txt.substring(real_close_idx, real_close_idx + frst_m_close[0].length)
         .replace(frst_m_close[1], frst_m_close[1] + guid) +
         txt.substring(real_close_idx + frst_m_close[0].length);
   }
   return arr;
}

Использование:

var txt = '<table>' +
   '<tr><td>1' +
   '<table><tr><td>2' +
   '<table><tr><td>3' +
   '</td></tr></table><table>inner_3</table>' +
   '</td></tr></table>' +
   '</td></tr>' +
   '</table>' +
   '<table>1st</table>' +
   '<table>2nd</table>';
var arr = get_arr_tags(txt, 'table');

Для вашего примера:

var txt = '<div class="nice">' +
   '<a href="http://www.google.com">Hello</a>' +
   '<div>World</div>' +
   '</div>';
var arr = get_arr_tags(txt, 'div');
1 голос
/ 06 января 2010

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

Вашу проблему невозможно решить с помощью традиционной концепции регулярных выражений, но некоторые движки, такие как .NET, немного обманывают и дают вам возможность сделать это с «определением балансирующей группы».

Вот учебник: http://www.codeproject.com/KB/recipes/Nested_RegEx_explained.aspx

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