考虑以下玩具示例。我希望将Go名称与正则表达式匹配,其中名称是由单a
分隔的字母序列#
,因此a#a#aaa
有效,但a#
或{{ 1}}不是。我可以通过以下两种方式编写正则表达式:
a##a
这两项都有效。现在考虑更复杂的匹配由单斜杠分隔的名称序列的任务。如上所述,我可以用两种方式编写代码:
r1 := regexp.MustCompile(`^a+(#a+)*$`)
r2 := regexp.MustCompile(`^(a+#)*a+$`)
其中N是带有^和$ stripped的名称的正则表达式。因为我有两个N的情况,所以现在我可以有4个正则表达式:
^N+(/N+)*$
^(N+/)*N+$
问题是为什么当匹配字符串 ^a+(#a+)*(/a+(#a+)*)*$
^(a+#)*a+(/a+(#a+)*)*$
^((a+#)*a+/)*a+(#a+)*$
^((a+#)*a+/)*(a+#)*a+$
时,第一个失败而其余3个案例按预期工作?即是什么导致第一个正则表达式不匹配?完整的code是:
"aa#a#a/a#a/a"
令人惊讶的是它会打印package main
import (
"fmt"
"regexp"
)
func main() {
str := "aa#a#a/a#a/a"
regs := []string {
`^a+(#a+)*(/a+(#a+)*)*$`,
`^(a+#)*a+(/a+(#a+)*)*$`,
`^((a+#)*a+/)*a+(#a+)*$`,
`^((a+#)*a+/)*(a+#)*a+$`,
}
for _, r := range(regs) {
fmt.Println(regexp.MustCompile(r).MatchString(str))
}
}
答案 0 :(得分:1)
您可以尝试减轻量词的原子子嵌套
如果你没有锚,表达式可能会爆炸
在回溯中如果使用这样的嵌套可选量词,
什么时候找不到直接的解决方案。
尝试这样的事情,看看它是否有效。
# ^a+(#?a)*(/a+(#?a)*)*$
^
a+
( # (1 start)
\#?
a
)* # (1 end)
( # (2 start)
/
a+
( # (3 start)
\#?
a
)* # (3 end)
)* # (2 end)
$
编辑 :(从注释中转换)如果复杂性太高,某些引擎甚至不会编译它,有些引擎会在运行时无声地失败,有些引擎会在回溯循环中锁定。
这个微妙的区别就是问题
糟糕:过于复杂(#?a+)*
好的:没有嵌套的,开放式的量词(#?a)*
如果您遇到此问题,请删除嵌套,通常解决问题 回溯问题。
eit2 :如果你需要一个分隔符,并且想要确保分隔符只在中间并被a
包围,你可以试试这个
https://play.golang.org/p/oM6B6H3Kdx
# ^a+[#/](a[#/]?)*a+$
^
a+
[\#/]
( # (1 start)
a
[\#/]?
)* # (1 end)
a+
$
或者
https://play.golang.org/p/WihqSjH_dI
# ^a+(?:[#/]a+)+$
^
a+
(?:
[\#/]
a+
)+
$
答案 1 :(得分:1)
这一定是golang regexp引擎中的一个错误。一个更简单的测试用例是^a(/a+(#a+)*)*$
在a/a#a
工作时无法匹配^(a)(/a+(#a+)*)*$
,请参阅http://play.golang.org/p/CDKxVeXW98。