代码之家  ›  专栏  ›  技术社区  ›  Re0sless

处理javascript regex子匹配

  •  1
  • Re0sless  · 技术社区  · 16 年前

    我试图编写一些javascript regex来用真正的html标记替换用户输入的标记,所以 [b] 会变成 <b> 等等。我用的正则表达式看起来是这样的

    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,}?) 彭定康也错了?

    谢谢

    8 回复  |  直到 7 年前
        1
  •  3
  •   A Nony Mouse    16 年前

    最简单的解决方案是替换所有标签,不管它们是否关闭,并让 .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
  •  1
  •   Eugen Anghel    16 年前

    恐怕你不能用正则表达式来表示递归。

    但是,您可以使用.NET的St.RealReaReXPress使用平衡匹配来实现这一点。更多信息请参见: http://blogs.msdn.com/bclteam/archive/2005/03/15/396452.aspx

    如果您使用的是.NET,您可能可以通过回调实现所需的功能。 如果没有,您可能需要滚动自己的小javascript解析器。

    再次重申,如果您有能力访问服务器,那么您可以使用完整的解析器。:)

    你要这个干嘛?如果不是为了预览,我强烈建议在服务器端进行处理。

        3
  •  0
  •   Marijn    16 年前

    您可以重复应用regexp,直到它不再匹配为止。这会产生一些奇怪的事情,比如“[b][b]foo[/b][/b]”=>“<b>[b]foo</b>[/b]”=>“<b><b>foo</b></b>”,但据我所见,最终结果仍然是一个具有匹配(尽管不一定正确嵌套)标记的合理字符串。

    或者如果你想做得“正确”,只需编写一个简单的递归下降解析器。虽然人们可能会期望[b] fo[u] bar [/b] bZ[/u]工作,但要用解析器来识别是很难的。

        4
  •  0
  •   Jonathan Lonowski    16 年前

    嵌套块没有被替换的原因是[b]的匹配将位置放在了[/b]之后。因此,所有那些((.){1,}?)然后忽略匹配项。

    在服务器端编写递归解析器是可能的——Perl使用 qr// 鲁比可能也有类似的东西。

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

    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
    

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

    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,}可以缩短为+
        5
  •  0
  •   Joe Hildebrand    16 年前

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

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

    更干净。注意我也改变了 .+? .* 是的。有两个问题 .+? 以下内容:

    1. 你不会匹配[u][/u],因为它们之间至少没有一个字符(+)
    2. 一个非贪婪的匹配不能很好地处理嵌套在其内部的同一个标记(?)
        6
  •  0
  •   vava    16 年前

    是的,你必须循环。或者,因为您的标记看起来非常像html标记,所以您可以替换 [b] 对于 <b> [/b] 对于 </b> 另外。(。{1,}?与(?*)相同吗?-也就是说,任何符号,最小可能的序列长度。

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

        7
  •  0
  •   Tim Cooper    13 年前

    怎么样:

    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
  •  0
  •   Tim Cooper    13 年前

    你是对的,内在的模式是麻烦的。

    ((.){1,}?)
    

    那就是至少进行一次捕获匹配,然后捕获整个事件。标签中的每个字符都将作为一个组捕获。

    您还可以在不需要结束元素名的情况下捕获它,并使用 {1} 当这是暗示的时候。以下是清理版本:

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

    对另一个问题不确定。