Golang正则表达式不支持复杂正则和预查问题解决
我有一个需求,需要匹配一段字符串中第几季的这个几,那么按正则表达式的语法,我的表达式应该是这样的
`(?<=第)\d+(?=季)`
然而,当我用go官方包regexp的时候,compile的时候报了个错误:
error parsing regexp: invalid or unsupported Perl syntax:
(?<
啥意思呢?
语法错误,不支持(?<,然而我在线运行的时候明明是没错的,这说明go的正则引擎不支持负向预查。
通过查看文档发现同时也是不支持正向预查的,下面这些都是不支持的。
(?=re) | before text matching re (NOT SUPPORTED) |
---|---|
(?!re) | before text not matching re (NOT SUPPORTED) |
(?<=re) | after text matching re (NOT SUPPORTED) |
(?<!re) | after text not matching re (NOT SUPPORTED) |
但是有些需求用预查确实很方便,比如我想查找“第3部”中的这个3,如果有预查我只需要
(?<=第)[0-9]+(?=部)
这样就可以很简单的查到第和部之间的这个数字,而没有预查的话难道要我先查到第再查部,记录他们的位置去查3吗?
这简直不科学!
我相信前辈们肯定遇到过这个问题而且已经造好轮子了,所以立马去GitHub搜,果然没让我失望,找到了
http://www.github.com/dlclark/regexp2
Regexp2 is a feature-rich RegExp engine for Go. It doesn’t have constant time guarantees like the built-in
regexp
package, but it allows backtracking and is compatible with Perl5 and .NET. You’ll likely be better off with the RE2 engine from theregexp
package and should only use this if you need to write very complex patterns or require compatibility with .NET.Regexp2是一个功能丰富的正则表达式引擎。 它没有像内置
regexp
包那样的固定时间保证,但是它允许回溯,并与Perl5和. NET 兼容。 如果你需要编写非常复杂的模式,或者需要与. NET. 引擎兼容,那么你最好从regexp
包中使用RE2引擎,并且应该只使用它。
用法:
用法与 go regexp
包类似。 首先通过 Compile
或者 MustCompile
方法将 正规表达式 转换为状态机。 然后,可以使用返回的regexp
结构体查找匹配项。 而且regexp结构是可以安全地跨goroutine使用的。
如何引入:
import “github.com/dlclark/regexp2”
go get -u github.com/dlclark/regexp2
比如:
var res string
str := "公众号:codeoffer。"
expr:= `(?<=公众号:).*(?=。)`
reg, _ := regexp2.Compile(expr, 0)
m, _ := reg.FindStringMatch(str)
if m != nil {
res = m.String()
}
fmt.Println(res)
输出:
codeoffer
这里和regexp的用法有稍微不一样的地方,比如Compile的时候需要传两个参数,第二个RegexOptions表示。。。
默认为0即可,然后匹配后返回的结果是一个group的结构,如果想真正获得结果需要进行一个string(),
组 0嵌入在匹配中。 组 0是一个自动分配的组,它包含整个 Pattern。 这意味着 m.String()
与 m.Group.String()
和 m.Groups()[0].String()
相同。
if m, _ := re.FindStringMatch(`Something to match`); m != nil {
// the whole match is always group 0
fmt.Printf("Group 0: %v\n", m.String())
// you can get all the groups too
gps := m.Groups()
// a group can be captured multiple times, so each cap is separately addressable
fmt.Printf("Group 1, first capture", gps[1].Captures[0].String())
fmt.Printf("Group 1, second capture", gps[1].Captures[1].String())
}
比较regexp和regexp2
Category | regexp | regexp2 |
---|---|---|
Catastrophic backtracking possible | no, constant execution time guarantees | yes, if your pattern is at risk you can use the re.MatchTimeout field |
Python-style capture groups (?P<name>re) | yes | no (yes in RE2 compat mode) |
.NET-style capture groups (?<name>re) or (?'name're) | no | yes |
comments (?#comment) | no | yes |
branch numbering reset (?|a|b) | no | no |
possessive match (?>re) | no | yes |
positive lookahead (?=re) | no | yes |
negative lookahead (?!re) | no | yes |
positive lookbehind (?<=re) | no | yes |
negative lookbehind (?<!re) | no | yes |
back reference \1 | no | yes |
named back reference \k'name' | no | yes |
named ascii character class [[:foo:]] | yes | no (yes in RE2 compat mode) |
conditionals (?(expr)yes|no) | no | yes |
regexp2功能还是很强大的,如果是复杂的正则表达式推荐使用。