匹配嵌套标签

时间:2010-01-05 21:46:34

标签: regex

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中,则代码会犯规。使分隔符不贪婪会使代码犯规更多,匹配到非常第一个关闭div标签。

那么任何想法?我经常想到这一点,我从来没有找到解决方案,这在正则表达式中是不可能的,还是只是我忘记了简单的事情?是不是有某种“回顾”机制?

7 个答案:

答案 0 :(得分:6)

impossible中的regex

改为使用HTML解析器,例如Beautiful Souphtml5libhpricotnokogiri

答案 1 :(得分:3)

.NET的Regex实现是为数不多的能够处理这种情况的实现之一。它提供balanced matching功能,可以使用和计算组来解析嵌套模式。

然而,这仍然不是一个完美的解决方案。例如,如果你将一个错误的html注释放入混合中,那么即使一个聪明的正则表达式与平衡匹配也会失败。因此,使用html解析器仍然会更好。

答案 2 :(得分:3)

平衡匹配似乎是非常合适的工具,并且可能可以用多种语言实现,但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 :(得分:2)

通常的建议不适用于HTML的regexp,因为HTML 不是常规的。因此,尝试使用正则表达式解析它(特别是对于像上面那样严格的事情)将会遇到困难。

答案 4 :(得分:2)

不是我建议使用它,而是:

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

应该有效,匹配任何标签,可以调整以匹配特定标签。

答案 5 :(得分:1)

正如其他人所说,这通常是一个坏主意。但是你说你只是出于好奇而问,所以这就是......

使用传统的正则表达式概念无法解决您的问题,但是某些引擎(如.NET)会作弊,并通过“平衡组定义”为您提供一种方法。

这是一个教程:http://www.codeproject.com/KB/recipes/Nested_RegEx_explained.aspx

答案 6 :(得分:1)

我的javascript正则表达式解决方案(сorrectly处理嵌套标签)

算法:

  1. 从所有正则表达式匹配到开始标记,我们采取最后一场比赛
  2. 在上次打开代码和代码本身之前暂时删除文字
  3. 在剩下的文字中,我们会查找第一个结束标记并将其标记为</tagnameGUID>
  4. 并重复其他比赛:)

    功能无法解析自动关闭代码

    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');