Golang 中使用正则一般就是使用 regexp 包。

这里只介绍一些常用操作,已经可以满足大部分开发需求,更多内容请详见官方文档 regexp package。

1. 正则表达式的语法

我们知道想要使用正则匹配,就需要严格按照正则语法设定正则匹配字符串,即 Pattern。

正则语法篇幅较长,这里就不介绍了,并且 Golang 官方的正则语法文档已经非常全面了,可以自己看看 regexp/syntax。

2. regexp 的用法

regexp 包的主要使用方式有两种:

RegexpRegexp

第 1 种方式能做的事很少,一共仅有 4 个方法,但却是正则操作里很常用方法。
第 2 种方式相对复杂,但提供了非常多的方法,几乎能够满足正则匹配各种场景下的所有需求。

两种方式没有优劣之分,一般来说,简单需求用方式 1,方式 1 无法满足的时候用方式 2,就可以了。

下面第 3 小节对应方式 1,第 4小节对应方式 2。

3. 使用 regexp 包内的全局方法

regexp 包内的全局方法一共有四个。

[]byte

函数声明:

1
func Match(pattern string, b []byte) (matched bool, err error)

用法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
"regexp"
)

func main() {
matched, err := regexp.Match(`foo.*`, []byte(`seafood`))
fmt.Println(matched, err)
matched, err = regexp.Match(`bar.*`, []byte(`seafood`))
fmt.Println(matched, err)
matched, err = regexp.Match(`a(b`, []byte(`seafood`))
fmt.Println(matched, err)

}

输出:

1
2
3
true <nil>
false <nil>
false error parsing regexp: missing closing ): `a(b`
io.RuneReader

函数声明:

1
func MatchReader(pattern string, r io.RuneReader) (matched bool, err error)

这个方法似乎不是很常用,也很简单,这里就不举例了。

string

函数声明:

1
func MatchString(pattern string, s string) (matched bool, err error)

用法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"regexp"
)

func main() {
matched, err := regexp.MatchString(`foo.*`, "seafood")
fmt.Println(matched, err)
matched, err = regexp.MatchString(`bar.*`, "seafood")
fmt.Println(matched, err)
matched, err = regexp.MatchString(`a(b`, "seafood")
fmt.Println(matched, err)
}

输出:

1
2
3
true <nil>
false <nil>
false error parsing regexp: missing closing ): `a(b`

3.4. 转义 Pattern

有些时候,我们的 Pattern 字符串里,本身就含有一些正则符号,如果不处理直接使用,就会被当做正则语法进行匹配,这不是我们期望的。

regexp 提供了一个方法,可以把一个字符串里所有与正则语法一致的字符进行转义,这样作为 Pattern 的时候就不会错误匹配了,

函数声明:

1
func QuoteMeta(s string) string

用法示例:

1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
"regexp"
)

func main() {
fmt.Println(regexp.QuoteMeta(`Escaping symbols like: .+*?()|[]{}^$`))
}

输出:

1
Escaping symbols like: \.\+\*\?\(\)\|\[\]\{\}\^\$

4. 通过 Regexp 类进行正则相关操作

4.1. 编译解析正则表达式(必须)

我们不会手动创建一个 Regexp 类型的对象,而是通过 regexp 包提供的方法编译解析正则表达式,并得到一个 Regexp 类的对象。

MustCompile()
1
func MustCompile(str string) *Regexp
str

这一步是必须的,我们必须通过此方法获得的 Regexp 类对象进行各类正则操作。

代码示例如下:

1
2
3
4
5
6
7
package main

import "regexp"

func main() {
re := regexp.MustCompile(`regular_expression`)
}
rereregular_expression`"

-

我们调用上面的方法编译解析正则表达式以后,就一直使用 Regexp 对象操作了,不过也许也会在某处想知道用来初始化 Regexp 对象的原始正则字符串是哪个,就会用到下面这个方法:

1
func (re *Regexp) String() string

这个就不写代码示例了,太简单了,就是直接返回用来初始化 Regexp 对象的原始正则字符串。

-

另外三个编译解析正则表达式的方法是:

1
2
3
func Compile(expr string) (*Regexp, error)
func CompilePOSIX(expr string) (*Regexp, error)
func MustCompilePOSIX(str string) *Regexp

这里简单说一下 4 个编译解析正则表达式的方法的区别:

Compile()CompilePOSIX()errMustCompile()MustCompilePOSIX()
Compile()CompilePOSIX()
CompilePOSIX()MustCompilePOSIX()
MustCompile()MustCompile()

-

下面说 Regexp 类对象支持的一些常用正则操作,注意这里只有我认为比较常用的,一些比较复杂的用法详见官方文档 regexp package。

4.2. 仅检查字符串是否包含正则表达式的任何匹配

regexp 有两个方法检查字符串是否包含正则表达式的任何匹配:

1
func (re *Regexp) Match(b []byte) bool
1
func (re *Regexp) MatchString(s string) bool
[]bytestring
MatchString()
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
"regexp"
)

func main() {
re := regexp.MustCompile(`(gopher){2}`)
fmt.Println(re.MatchString("gopher"))
fmt.Println(re.MatchString("gophergopher"))
fmt.Println(re.MatchString("gophergophergopher"))
}

输出:

1
2
3
false
true
true

4.3. 返回字符串中匹配的首个子串

Match()MatchString()
1
func (re *Regexp) Find(b []byte) []byte
1
func (re *Regexp) FindString(s string) string
[]bytestringFind()nilFindString()

返回的首个符合条件的子串,也就是最左边的。

?*
FindString()
1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"regexp"
)

func main() {
re := regexp.MustCompile(`foo.?`)
fmt.Printf("%q\n", re.FindString("seafood fool"))
fmt.Printf("%q\n", re.FindString("meat"))
}

输出:

1
2
"food"
""
foo.??FindString()?

4.4. 返回字符串中匹配的所有子串

Find()FindString()
1
func (re *Regexp) FindAll(b []byte, n int) [][]byte
1
func (re *Regexp) FindAllString(s string, n int) []string
[]bytestringnil
nn

注意这里的匹配长度规则,多个匹配子串之间是不会重叠的,每个匹配串的长度偏好同样与正则符号的偏好一致。

FindAllString()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
"regexp"
)

func main() {
re := regexp.MustCompile(`a.`)
fmt.Println(re.FindAllString("paranormal", -1))
fmt.Println(re.FindAllString("paranormal", 2))
fmt.Println(re.FindAllString("graal", -1))
fmt.Println(re.FindAllString("none", -1))
}

输出:

1
2
3
4
[ar an al]
[ar an]
[aa]
[]

4.5. 返回字符串中匹配子串的索引

Find()FindString()FindAll()FindAllString()

我们我们要想知道盘匹配的字符串的起始索引,就需要下面的方法:

1
func (re *Regexp) FindIndex(b []byte) (loc []int)
1
func (re *Regexp) FindStringIndex(s string) (loc []int)
1
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
1
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
Find()FindString()FindAll()FindAllString()
FindIndex()FindStringIndex()loc[0]loc[1][loc[0], loc[1])FindAllIndex()FindAllStringIndex()nil
FindStringIndex()FindAllStringIndex()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
"regexp"
)

func main() {
re := regexp.MustCompile(`ab.?`)
fmt.Println(re.FindString("tablett"))
fmt.Println(re.FindStringIndex("tablett"))
fmt.Println(re.FindAllString("abcabdab", -1))
fmt.Println(re.FindAllStringIndex("abcabdab", -1))
}

输出:

1
2
3
4
abl
[1 4]
[abc abd ab]
[[0 3] [3 6] [6 8]]

4.6. 使用正则分割字符串

Regexp 对象也提供一个使用正则分割字符串的方法,即用正则匹配分隔符或分隔字符串。方法声明如下:

1
func (re *Regexp) Split(s string, n int) []string

此方法接受两个参数:

snn > 0n = 0niln < 0

返回值为分割后的字符串数组。

下面看一段代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"fmt"
"regexp"
)

func main() {
a := regexp.MustCompile(`a`)
fmt.Println(a.Split("banana", -1))
fmt.Println(a.Split("banana", 0))
fmt.Println(a.Split("banana", 1))
fmt.Println(a.Split("banana", 2))

zp := regexp.MustCompile(`z+`)
fmt.Println(zp.Split("pizza", -1))
fmt.Println(zp.Split("pizza", 0))
fmt.Println(zp.Split("pizza", 1))
fmt.Println(zp.Split("pizza", 2))

s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5) // s: ["", "b", "b", "c", "cadaaae"]
fmt.Println(s)
}

输出:

1
2
3
4
5
6
7
8
9
[b n n ]
[]
[banana]
[b nana]
[pi a]
[]
[pizza]
[pi a]
[ b b c cadaaae]

5. 扩展:Regexp 的并发安全性

ReplaceAll()
Longest()
Longest()