处理Javascript RegEx子匹配

时间:2020-03-05 18:58:14  来源:igfitidea点击:

我正在尝试编写一些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>"