处理Javascript RegEx子匹配
我正在尝试编写一些JavaScript RegEx来用真实的html标签替换用户输入的标签,因此[b]
将变为<b>等。我正在使用的RegEx看起来像这样
var exptags = /\[(b|u|i|s|center|code){1}]((.){1,}?)\[\/(){1}]/ig;
使用以下JavaScript
s.replace(exptags,"<></>");
这适用于单个嵌套标签,例如:
[b]hello[/b] [u]world[/u]
但是如果标签相互嵌套,则仅与外部标签匹配,例如
[b]foo [u]to the[/u] bar[/b]
这只会匹配b
标签。我怎样才能解决这个问题?我是否应该循环播放,直到起始字符串与结果相同为止?我感觉((。){1,}?)
patten也错了吗?
谢谢
解决方案
回答
AFAIK,我们不能使用正则表达式来表示递归。
但是,我们可以使用.NET的System.Text.RegularExpressions使用平衡匹配来做到这一点。在此处查看更多信息:http://blogs.msdn.com/bclteam/archive/2005/03/15/396452.aspx
如果我们使用的是.NET,则可能可以通过回调实现所需的功能。
如果没有,我们可能必须滚动自己的小javascript解析器。
再说一次,如果我们有能力使用服务器,则可以使用完整的解析器。 :)
无论如何,我们需要什么?如果除了预览以外还用于其他用途,我强烈建议我们在服务器端进行处理。
回答
是的,我们将不得不循环。另外,由于标记看起来很像HTML标记,因此我们可以分别将[b]
替换为<b>,将[[/ b]
替换为</ b>`。 (。){1,}?与(。*?)相同,即任何符号,最小可能的序列长度。
更新:感谢MrP,(。){1,}?是(。)+ ?,我不好。
回答
我们对内部模式感到麻烦是正确的。
((.){1,}?)
那就是至少进行一次捕获的匹配,然后捕获整个事物。标签内的每个字符都将被捕获为一个组。
我们也可以在不需要结束符名称时捕获它,并且在暗示时使用{1}
。以下是清理版本:
/\[(b|u|i|s|center|code)](.+?)\[\/]/ig
不确定其他问题。
回答
我们可以重复应用正则表达式,直到不再匹配为止。那会做奇怪的事情,例如" [b] [b] foo [/ b] [/ b]" =>" <b> [b] foo </ b> [/ b]" =>" <b> <b > foo </ b> </ b>",但据我所见,最终结果仍然是带有匹配标签(尽管不一定正确嵌套)的明智字符串。
或者,如果我们想"正确"地做它,只需编写一个简单的递归下降解析器。尽管人们可能希望" [b] foo [u] bar [/ b] baz [/ u]"能够正常工作,但是使用解析器很难识别。
回答
无法替换嵌套块的原因是因为[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}]/ig; while (s.match(exptags)) { s = s.replace(exptags, "<></>"); } 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)\](.+?)\[\/()\]/ig;
- 如果不存在其他计数说明符,则假定为{1}
- {1,}可以缩短为+
回答
同意Richard Szalay的观点,但他的正则表达式未正确引用:
var exptags = /\[(b|u|i|s|center|code)](.*)\[\/]/ig;
更干净。请注意,我还将。+?
更改为。*
。 ..有两个问题:
- 我们不会匹配[u] [/ u],因为它们之间至少没有一个字符(+)
- 非贪婪的匹配与嵌套在其内部的相同标签的匹配效果不佳(?)
回答
怎么样:
tagreg=/\[(.?)?(b|u|i|s|center|code)\]/gi; "[b][i]helloworld[/i][/b]".replace(tagreg, "<>"); "[b]helloworld[/b]".replace(tagreg, "<>");
对我来说,上面的结果是:
<b><i>helloworld</i></b> <b>helloworld</b>
这似乎可以完成我们想要的操作,并且具有只需要一次通过的优点。
免责声明:我不经常用JS编写代码,所以如果我犯了任何错误,请随时指出:-)
回答
最简单的解决方案是替换所有标签,无论它们是否关闭,并让.innerHTML找出是否匹配,这样做会更具弹性。
var tagreg = /\[(\/?)(b|u|i|s|center|code)]/ig div.innerHTML="[b][i]helloworld[/b]".replace(tagreg, "<>") //no closing i //div.inerHTML=="<b><i>helloworld</i></b>"