在golang中使用嵌套组的regexp问题

时间:2015-07-27 21:13:47

标签: regex go re2

考虑以下玩具示例。我希望将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)) } }

2 个答案:

答案 0 :(得分:1)

您可以尝试减轻量词的原子子嵌套 如果你没有锚,表达式可能会爆炸 在回溯中如果使用这样的嵌套可选量词,
什么时候找不到直接的解决方案。

Go可能会对此感到厌恶,迫使它彻底失败而不是 大规模的回溯,但不确定。

尝试这样的事情,看看它是否有效。

 # ^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

我提交了https://github.com/golang/go/issues/11905