【问题标题】:Processing Javascript RegEx submatches处理 Javascript RegEx 子匹配
【发布时间】:2010-09-10 00:08:39
【问题描述】:

我正在尝试编写一些 JavaScript RegEx 来用真正的 html 标签替换用户输入的标签,所以[b] 将变为<b> 等等。我使用的 RegEx 看起来像这样

var exptags = /\[(b|u|i|s|center|code){1}]((.){1,}?)\[\/(\1){1}]/ig;

使用以下 JavaScript

s.replace(exptags,"<$1>$2</$1>");

这适用于单个嵌套标签,例如:

[b]hello[/b] [u]world[/u]

但如果标签相互嵌套,它只会匹配外部标签,例如

[b]foo [u]to the[/u] bar[/b]

这只会匹配b 标签。我怎样才能解决这个问题?我应该循环直到起始字符串与结果相同吗?我感觉((.){1,}?)的模式也错了?

谢谢

【问题讨论】:

标签: javascript regex markdown


【解决方案1】:

最简单的解决方案是替换所有标签,无论它们是否关闭,让.innerHTML 解决它们是否匹配,这样会更有弹性..

var tagreg = /\[(\/?)(b|u|i|s|center|code)]/ig
div.innerHTML="[b][i]helloworld[/b]".replace(tagreg, "<$1$2>") //no closing i
//div.inerHTML=="<b><i>helloworld</i></b>"

【讨论】:

    【解决方案2】:

    AFAIK 你不能用正则表达式表达递归。

    但是,您可以使用 .NET 的 System.Text.RegularExpressions 使用平衡匹配来做到这一点。在此处查看更多信息:http://blogs.msdn.com/bclteam/archive/2005/03/15/396452.aspx

    如果您使用的是 .NET,您可能可以通过回调来实现您所需要的。 如果没有,您可能必须推出自己的小型 javascript 解析器。

    再说一次,如果你有能力访问服务器,你可以使用完整的解析器。 :)

    你需要这个做什么?如果它是用于预览以外的任何内容,我强烈建议在服务器端进行处理。

    【讨论】:

    • 是的,它用于 cmets 区域的实时预览,服务器端它的 PHP,但有该位的代码。
    【解决方案3】:

    您可以重复应用正则表达式,直到它不再匹配。这会做一些奇怪的事情,比如 "[b][b]foo[/b][/b]" => "[b]foo[/b]" => "foo”,但据我所见,最终结果仍然是一个带有匹配(尽管不一定正确嵌套)标签的合理字符串。

    或者,如果您想“正确”地做,只需编写一个简单的递归下降解析器。尽管人们可能期望 "[b]foo[u]bar[/b]baz[/u]" 能够工作,但使用解析器很难识别。

    【讨论】:

      【解决方案4】:

      嵌套块没有被替换的原因是因为匹配,对于 [b],将位置放在 [/b] 之后。因此,所有 ((.){1,}?) 匹配的内容都会被忽略。

      可以在服务器端编写递归解析器——Perl 使用qr//,而 Ruby 可能也有类似的东西。

      不过,您不一定需要真正的递归。您可以使用相对简单的循环来等效地处理字符串:

      var s = '[b]hello[/b] [u]world[/u] [b]foo [u]to the[/u] bar[/b]';
      var exptags = /\[(b|u|i|s|center|code){1}]((.){1,}?)\[\/(\1){1}]/ig;
      
      while (s.match(exptags)) {
         s = s.replace(exptags, "<$1>$2</$1>");
      }
      
      document.writeln('<div>' + s + '</div>'); // after
      

      在这种情况下,它将通过 2 次:

      0: [b]hello[/b] [u]world[/u] [b]foo [u]to the[/u] bar[/b]
      1: <b>hello</b> <u>world</u> <b>foo [u]to the[/u] bar</b>
      2: <b>hello</b> <u>world</u> <b>foo <u>to the</u> bar</b>
      

      另外,关于清理 RegEx 的一些建议:

      var exptags = /\[(b|u|i|s|center|code)\](.+?)\[\/(\1)\]/ig;
      
      • 在不存在其他计数说明符时假定为 {1}
      • {1,} 可以缩写为 +

      【讨论】:

      • 如果您将 [center][/center] 添加到您的测试用例中,并将一个标签嵌套在另一个标签中,并从下面获取我的正则表达式,我会投票赞成您的答案。
      • “将一个标签嵌套在另一个标签中”,我的意思是“同一标签在其自身内部”,例如:[b] foo [b]bar[/b] baz[/b]
      【解决方案5】:

      同意 Richard Szalay,但他的正则表达式没有被正确引用:

      var exptags = /\[(b|u|i|s|center|code)](.*)\[\/\1]/ig;
      

      更干净。请注意,我还将.+? 更改为.*.+?有两个问题:

      1. 您不会匹配 [u][/u],因为它们之间至少没有一个字符 (+)
      2. 非贪婪匹配不会很好地处理嵌套在自身内部的相同标记 (?)

      【讨论】:

        【解决方案6】:

        是的,您必须循环播放。或者,由于您的标签看起来很像 HTML 的标签,您可以分别将 [b] 替换为 &lt;b&gt;[/b] 替换为 &lt;/b&gt;。 (.){1,}?与 (.*?) 相同 - 即任何符号,最小可能的序列长度。

        更新:感谢 MrP,(.){1,}?是 (.)+?,我的错。

        【讨论】:

          【解决方案7】:

          怎么样:

          tagreg=/\[(.?)?(b|u|i|s|center|code)\]/gi;
          "[b][i]helloworld[/i][/b]".replace(tagreg, "<$1$2>");
          "[b]helloworld[/b]".replace(tagreg, "<$1$2>");
          

          对我来说,以上产生:

          <b><i>helloworld</i></b>
          <b>helloworld</b>
          

          这似乎可以满足您的要求,并且具有只需要一次通过的优势。

          免责声明:我不经常用 JS 编写代码,所以如果我犯了任何错误,请随时指出:-)

          【讨论】:

            【解决方案8】:

            关于内部模式很麻烦,你说得对。

            ((.){1,}?)
            

            即至少进行一次捕获的匹配,然后捕获整个事物。标签中的每个字符都将被捕获为一个组。

            当你不需要它时,你也会捕获你的结束元素名称,并在暗示时使用{1}。以下是清理版:

            /\[(b|u|i|s|center|code)](.+?)\[\/\1]/ig
            

            不确定其他问题。

            【讨论】:

              猜你喜欢
              • 2010-09-10
              • 2012-02-22
              • 1970-01-01
              • 2015-12-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-03-06
              相关资源
              最近更新 更多