在<pre> and </pre>内匹配<pre></pre>

时间:2014-05-17 22:51:46

标签: javascript regex

不可能寻找任务。我有一个服务器输出,其中包含<pre>内的转储。不幸的是,我碰巧转储了一个包含一些html标签的文件。我需要将任何内部</pre>转换为HTML实体,以便在将数据附加到DOM时不会破坏structire:

   <pre> 
      ...
      echo '<pre>'
      cat gcc.log
      echo '</pre>'
      ...
   </pre>

但是有一个明显的规则 - 在echo '<pre>之前始终会有</pre>。但它可能不完全是echo '</pre>

基于此,我构建了非常复杂的正则表达式:

   <pre>   - The beginning tag
       ([\s\S]*?) - Any characters including new lines
           (?:(echo[^\n]+) - Echo and anything but new line
               (<pre>|<\/pre>|<\/xmp>|<xmp>)) - The enclosing tags
           ([\s\S]*?) - More random characters
   <\/pre>

问题是,只要代码中有两个</pre>,regexp就会匹配第一个,并将第二个视为随机字符 - ([\s\S]*?)

如何让正则表达式首先尝试匹配显式字符然后匹配.*?内容?

你可以在http://regex101.com

上试一试

哦,我无法在服务器上修复它,真的

2 个答案:

答案 0 :(得分:2)

这可能有用(假设标签在引号内并且引号特别平衡):

var html = "<pre>\n      ...\n      echo '<pre>'\n      cat gcc.log\n      echo '</pre>'\n      ...\n</pre>";

html = html.replace(/(<pre[^>]*>)((?:(?=((["'])(?:(?=(\\.|[^\\"']+|(?!\5)["']))\4)*\5|[^'<]+|<(?!\/pre>)))\3)*)<\/pre>/g, 
                    function(_, g1, g2) {
                        g2 = g2.replace(/</g, '&lt;');
                        g2 = g2.replace(/>/g, '&gt;');
                        return g1 + g2 + '</pre>';
                    }
                   );

console.log(html);

此模式使用此技巧模拟原子组:(?>pattern) =&gt; (?:(?=(pattern))\1)使用前瞻的内容自然是原子的事实,以避免灾难性的回溯。

答案 1 :(得分:2)

这是一个兼容JavaScript的正则表达式,它匹配一个只有一个嵌套PRE元素的PRE元素(以Python自由空格形式显示,带有大量注释,以便您可以理解它是如何工作的):

re_pre_inside_pre = re.compile(r"""
    # Match PRE element containing one nested PRE element.
    (<pre\b[^>]*>)      # $1: Outer PRE start tag.
    (                   # $2: Outer PRE element contents.
      (                 # $3: Stuff from <PRE> to <PRE>.
        [^<]*           # (normal*) Zero or more non-tags.
        (?:             # Begin ((special normal*)*).
          <             # (special) Any other tags,
          (?!\/?pre\b)  # but not a <PRE> or </PRE>.
          [^<]*         # More (normal*).
        )*              # End ((special normal*)*).
      )                 # End $3: Stuff from <PRE> to <PRE>.
      (<pre\b[^>]*>)    # $4: Inner PRE start tag.
      (                 # $5: Inner PRE element contents.
        [^<]*           # (normal*) Zero or more non-tags.
        (?:             # Begin ((special normal*)*).
          <             # (special) Any other tags,
          (?!\/?pre\b)  # but not a <PRE> or </PRE>.
          [^<]*         # More (normal*).
        )*              # End ((special normal*)*).
      )                 # End $5: Inner PRE element contents.
      (</pre\b\s*>)     # $6: Inner PRE end tag.
      (                 # $7: Stuff from </PRE> to </PRE>.
        [^<]*           # (normal*) Zero or more non-tags.
        (?:             # Begin ((special normal*)*).
          <             # (special) Any other tags,
          (?!\/?pre\b)  # but not a <PRE> or </PRE>.
          [^<]*         # More (normal*).
        )*              # End ((special normal*)*).
      )                 # End $7: Stuff from </PRE> to </PRE>.
    )                   # End $2: Outer PRE element contents.
    (</pre\b\s*>)       # $8: Outer PRE end tag.
    """, re.VERBOSE | re.IGNORECASE)

请注意:normal* (special normal*)*部分是展开的循环 - 效率技术取自Jeffrey Friedl的Mastering Regular Expressions (3rd Edition)

还请注意捕获组:
$1: - 外部PRE开始标记 $2: - 外部PRE元素的内容 $3: - 从<PRE><PRE>的内容 $4: - 内部PRE开始标记 $5: - 内部PRE元素的内容 $6: - 内部PRE结束标记 $7: - 从</PRE></PRE>的内容 $8: - 外部PRE结束标记。

这是一个经过测试的JavaScript函数,它利用上面的正则表达式将外部PRE内容中的所有尖括号(即组$2的内容)转换为HTML实体:

// Process PRE element containing one nested PRE element.
function processNestedPRE(text) {
    var re_pre_inside_pre = /(<pre\b[^>]*>)(([^<]*(?:<(?!\/?pre\b)[^<]*)*)(<pre\b[^>]*>)([^<]*(?:<(?!\/?pre\b)[^<]*)*)(<\/pre\b\s*>)([^<]*(?:<(?!\/?pre\b)[^<]*)*))(<\/pre\b\s*>)/gi;
    return text.replace(re_pre_inside_pre,
        function(m0, m1, m2, m3, m4, m5, m6, m7, m8){
            // m2 has the outer PRE contents.
            // Convert all its <> angle brackets to entities:
            m2 = m2.replace(/[<>]/g,
                // Use a literal object for conversion.
                function(n0){ return {'<': '&lt;', '>': '&gt;'}[n0]; });
            // Put humpty dumpty back together again.
            return m1 + m2 + m8;
        });
}

不知道哪些部分需要验证 - 这就是为什么我已经包含了所有捕获组,以便您可以修改替换功能以仅执行所需的部分。

希望这有帮助。