Go 标准库之 regexp

https://pkg.go.dev/regexp

通过正则判断是否匹配

regexp 包中含有三个函数用来判断是否匹配,如果匹配返回true,否则返回false

1
2
3
4
5
6
7
8
9
10
// Match 检查 b 中是否存在匹配 pattern 的子序列。更复杂的用法请使用Compile函数和Regexp对象。
func Match(pattern string, b []byte) (matched bool, error error)


// MatchString 类似 Match,但匹配对象是字符串。
func MatchReader(pattern string, r io.RuneReader) (matched bool, error error)


// MatchReader 类似 Match,但匹配对象是 io.RuneReader。
func MatchString(pattern string, s string) (matched bool, error error)

示例:

1
2
3
4
5
6
7
// 验证一个输入是不是IP地址
func IsIP(ip string) (b bool) {
if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m {
return false
}
return true
}

通过正则获取内容

Match 模式只能用来对字符串的判断,而无法截取字符串的一部分、过滤字符串、或者提取出符合条件的一批字符串。如果想要满足这些需求,那就需要使用正则表达式的复杂模式。

解析正则表达式,生成 pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Compile解析并返回一个正则表达式。如果成功返回,该Regexp就可用于匹配文本。
// 在匹配文本时,该正则表达式会尽可能早的开始匹配,并且在匹配过程中选择回溯搜索到的第一个匹配结果。这种模式被称为“leftmost-first”,Perl、Python和其他实现都采用了这种模式,但本包的实现没有回溯的损耗。
func Compile(expr string) (*Regexp, error)


// 类似Compile但会将语法约束到POSIX ERE(egrep)语法,并将匹配模式设置为leftmost-longest。
// 在匹配文本时,该正则表达式会尽可能早的开始匹配,并且在匹配过程中选择搜索到的最长的匹配结果。这种模式被称为“leftmost-longest”,POSIX采用了这种模式(早期正则的DFA自动机模式)。
// 然而,可能会有多个“leftmost-longest”匹配,每个都有不同的组匹配状态,本包在这里和POSIX不同。在所有可能的“leftmost-longest”匹配里,本包选择回溯搜索时第一个找到的,而POSIX会选择候选结果中第一个组匹配最长的(可能有多个),然后再从中选出第二个组匹配最长的,依次类推。POSIX规则计算困难,甚至没有良好定义。
// 参见 http://swtch.com/~rsc/regexp/regexp2.html#posix 获取细节。
func CompilePOSIX(expr string) (*Regexp, error)


// MustCompile类似Compile但会在解析失败时panic,主要用于全局正则表达式变量的安全初始化。
func MustCompile(str string) *Regexp


// MustCompilePOSIX类似CompilePOSIX但会在解析失败时panic,主要用于全局正则表达式变量的安全初始化。
func MustCompilePOSIX(str string) *Regexp

上面的函数用于解析正则表达式是否合法,如果正确,则会返回一个Regexp,然后就能够利用该对象在任意字符串上执行需要的操作

带POSIX后缀的不同点在于其使用POSIX语法,该语法使用最长最左方式搜索,而不带该后缀的方法是采用最左方式搜索(如[a-z]{2,4}这样的正则表达式,应用于”aa09aaa88aaaa”这个文本串时,带POSIX后缀的将返回aaaa,不带后缀的则返回aa)。

前缀有Must的函数表示在解析正则表达式时,如果匹配模式串不满足正确的语法则直接panic,而不加Must前缀的将只是返回错误

解析完正则表达式后能够进行的操作有

查找操作 ———— 即前缀带有Find的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func (re *Regexp) Find(b []byte) []byte
func (re *Regexp) FindAll(b []byte, n int) [][]byte
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
func (re *Regexp) FindAllString(s string, n int) []string
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
func (re *Regexp) FindIndex(b []byte) (loc []int)
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
func (re *Regexp) FindString(s string) string
func (re *Regexp) FindStringIndex(s string) (loc []int)
func (re *Regexp) FindStringSubmatch(s string) []string
func (re *Regexp) FindStringSubmatchIndex(s string) []int
func (re *Regexp) FindSubmatch(b []byte) [][]byte
func (re *Regexp) FindSubmatchIndex(b []byte) []int

上面这18个函数我们根据输入源(byte slice、string 和 io.RuneReader)不同还可以继续简化成如下几个,其他的只是输入源不一样,其他功能基本是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 返回保管正则表达式 re 在 b 中的最左侧的一个匹配结果的 []byte 切片。如果没有匹配到,会返回nil。
func (re *Regexp) Find(b []byte) []byte


// 下面函数跟上面的函数的差别在与能够使用第二个参数来决定返回几个匹配结果
// 返回保管正则表达式 re 在 b 中的所有不重叠的匹配结果的[][]byte切片。如果没有匹配到,会返回nil。
func (re *Regexp) FindAll(b []byte, n int) [][]byte


// 下面函数的作用是查找索引
// 返回保管正则表达式re在b中的最左侧的一个匹配结果的起止位置的切片(显然len(loc)==2)。匹配结果可以通过起止位置对b做切片操作得到:b[loc[0]:loc[1]]。如果没有匹配到,会返回nil。
func (re *Regexp) FindIndex(b []byte) (loc []int)


// 下面函数和上面的函数的区别在与能够使用第二个参数决定返回匹配的数量
// 返回保管正则表达式re在b中的所有不重叠的匹配结果的起止位置的切片
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int


//下面函数使用在正则表达式有分组时,切片中的第一个索引值为整个输入字符串满足正则表达式的输出,后面的索引值则为满足分组的字符串
// 返回一个保管正则表达式re在b中的最左侧的一个匹配结果以及(可能有的)分组匹配的结果的\[\]\[\]byte切片。如果没有匹配到,会返回nil。
func (re *Regexp) FindSubmatch(b []byte) [][]byte


// 下面函数和上面函数的区别在与能够使用第二个参数决定返回匹配的数量:
// 返回一个保管正则表达式re在b中的所有不重叠的匹配结果及其对应的(可能有的)分组匹配的结果的\[\]\[\]\[\]byte切片。如果没有匹配到,会返回nil。
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte


// 下面函数实现分组匹配查找索引:
//返回一个保管正则表达式re在b中的最左侧的一个匹配结果以及(可能有的)分组匹配的结果的起止位置的切片。匹配结果和分组匹配结果可以通过起止位置对b做切片操作得到:b\[loc\[2\*n\]:loc\[2\*n+1\]\]。如果没有匹配到,会返回nil。
func (re *Regexp) FindSubmatchIndex(b []byte) []int


// 下面函数和上面函数的区别在与能够使用第二个参数决定返回匹配的数量
// 返回一个保管正则表达式re在b中的所有不重叠的匹配结果及其对应的(可能有的)分组匹配的结果的起止位置的切片(第一层表示第几个匹配结果,完整匹配和分组匹配的起止位置对在第二层)。如果没有匹配到,会返回nil。
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
re := regexp.MustCompile("fo.?")
fmt.Printf("%q\n", re.FindString("seafood")) //"foo"
fmt.Printf("%q\n", re.Find([]byte("seafood"))) //"foo"


re := regexp.MustCompile("a.")
fmt.Println(re.FindAllString("paranormal", -1)) //-1表示返回所有匹配的值,[ar an al]
fmt.Println(re.FindAllString("paranormal", 2)) //2表示返回2个匹配的值,[ar an]
fmt.Println(re.FindAllString("paranormal", 1)) //1表示返回1个匹配的值,[ar]
fmt.Println(re.FindAllString("paranormal", 0)) //0表示返回0个匹配的值,[]
fmt.Println(re.FindAllString("graal", -1)) //[aa]
fmt.Println(re.FindAllString("none", -1)) //[]


re := regexp.MustCompile("ab?")
fmt.Println(re.FindStringIndex("tablett")) //[1 3],表示匹配到的字符在"tablett"的[1,3]切片处
fmt.Println(re.FindStringIndex("foo") == nil) //没有匹配到会返回nil
fmt.Println(re.FindIndex([]byte("tablett"))) //[1 3],表示匹配到的字符在"tablett"的[1,3]切片处
fmt.Println(re.FindIndex([]byte("foo")) == nil) //没有匹配到会返回nil


re := regexp.MustCompile("a.")
fmt.Println(re.FindAllStringIndex("paranormal", -1)) //[[1 3] [3 5] [8 10]],-1表示匹配全部
fmt.Println(re.FindAllStringIndex("paranormal", 2)) //[[1 3] [3 5]] ,2表示返回匹配的两个的索引切片
fmt.Println(re.FindAllStringIndex("paranormal", 1)) //[[1 3]] ,1表示返回匹配的一个的索引切片
fmt.Println(re.FindAllIndex([]byte("paranormal"), -1)) //[[1 3] [3 5] [8 10]]
fmt.Println(re.FindAllIndex([]byte("paranormal"), 2)) //[[1 3] [3 5]]
fmt.Println(re.FindAllIndex([]byte("paranormal"), 1)) //[[1 3]]


re := regexp.MustCompile("a(x*)b(y|z)c")
fmt.Printf("%q\n", re.FindSubmatch([]byte("-axxxbyc-"))) //["axxxbyc" "xxx" "y"]
fmt.Printf("%q\n", re.FindSubmatch([]byte("-abzc-"))) //["abzc" "" "z"]
fmt.Printf("%q\n", re.FindSubmatch([]byte("-aczc-"))) //[],整个都不匹配,更没有分组匹配,将返回空数组
fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-")) //["axxxbyc" "xxx" "y"]
fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-")) //["abzc" "" "z"]
fmt.Printf("%q\n", re.FindStringSubmatch("-aczc-")) //[],整个都不匹配,更没有分组匹配,将返回空数组

re := regexp.MustCompile("a(x*)b(y|z)c")
fmt.Printf("%q\n", re.FindAllSubmatch([]byte("-axxxbyc-axxbyc-axbyc-"), -1)) //[["axxxbyc" "xxx" "y"] ["axxbyc" "xx" "y"] ["axbyc" "x" "y"]]
fmt.Printf("%q\n", re.FindAllSubmatch([]byte("-axxxbyc-axxbyc-axbyc-"), 2)) //[["axxxbyc" "xxx" "y"] ["axxbyc" "xx" "y"]]
fmt.Printf("%q\n", re.FindAllSubmatch([]byte("-axxxbyc-axxbyc-axbyc-"), 1)) //[["axxxbyc" "xxx" "y"]]
fmt.Printf("%q\n", re.FindAllSubmatch([]byte("-abzc-abzc-"), -1)) //[["abzc" "" "z"] ["abzc" "" "z"]]
fmt.Printf("%q\n", re.FindAllSubmatch([]byte("-abzc-abzc-"), 1)) //[["abzc" "" "z"]]
fmt.Printf("%q\n", re.FindAllSubmatch([]byte("-aczc-"), -1)) //[],整个都不匹配,更没有分组匹配,将返回空数组

fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxxbyc-axxbyc-axbyc-", -1)) //[["axxxbyc" "xxx" "y"] ["axxbyc" "xx" "y"] ["axbyc" "x" "y"]]
fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxxbyc-axxbyc-axbyc-", 2)) //[["axxxbyc" "xxx" "y"] ["axxbyc" "xx" "y"]]
fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxxbyc-axxbyc-axbyc-", 1)) //[["axxxbyc" "xxx" "y"]]
fmt.Printf("%q\n", re.FindAllStringSubmatch("-abzc-abzc-", -1)) //[["abzc" "" "z"] ["abzc" "" "z"]]
fmt.Printf("%q\n", re.FindAllStringSubmatch("-abzc-abzc-", 1)) //[["abzc" "" "z"]]
fmt.Printf("%q\n", re.FindAllStringSubmatch("-aczc-", -1)) //[],整个都不匹配,更没有分组匹配,将返回空数组


re := regexp.MustCompile("a(x*)b(y|z)c")
fmt.Println(re.FindSubmatchIndex([]byte("-axxxbyc-"))) //[1 8 2 5 6 7],即整体匹配字符串"-axxxbyc-"的[1,8]切片,分组1匹配[2,5],分组2匹配[6,7]切片
fmt.Println(re.FindSubmatchIndex([]byte("-abzc-"))) //[1 5 2 2 3 4]
fmt.Println(re.FindSubmatchIndex([]byte("-aczc-"))) //[],整个都不匹配,更没有分组匹配,将返回空数组
fmt.Println(re.FindStringSubmatchIndex("-axxxbyc-")) //[1 8 2 5 6 7]
fmt.Println(re.FindStringSubmatchIndex("-abzc-")) //[1 5 2 2 3 4]
fmt.Println(re.FindStringSubmatchIndex("-aczc-")) //[],整个都不匹配,更没有分组匹配,将返回空数组


re := regexp.MustCompile("a(x*)b(y|z)c")
fmt.Println(re.FindAllSubmatchIndex([]byte("-axxxbyc-axxbyc-axbyc-"), -1)) //[[1 8 2 5 6 7] [9 15 10 12 13 14] [16 21 17 18 19 20]]
fmt.Println(re.FindAllSubmatchIndex([]byte("-axxxbyc-axxbyc-axbyc-"), 2)) //[[1 8 2 5 6 7] [9 15 10 12 13 14]]
fmt.Println(re.FindAllSubmatchIndex([]byte("-axxxbyc-axxbyc-axbyc-"), 1)) //[[1 8 2 5 6 7]]
fmt.Println(re.FindAllSubmatchIndex([]byte("-abzc-abzc-"), -1)) //[[1 5 2 2 3 4] [6 10 7 7 8 9]]
fmt.Println(re.FindAllSubmatchIndex([]byte("-abzc-abzc-"), 1)) //[[1 5 2 2 3 4]]
fmt.Println(re.FindAllSubmatchIndex([]byte("-aczc-"), -1)) //[],整个都不匹配,更没有分组匹配,将返回空数组

fmt.Println(re.FindAllStringSubmatchIndex("-axxxbyc-axxbyc-axbyc-", -1)) //[[1 8 2 5 6 7] [9 15 10 12 13 14] [16 21 17 18 19 20]]
fmt.Println(re.FindAllStringSubmatchIndex("-axxxbyc-axxbyc-axbyc-", 2)) //[[1 8 2 5 6 7] [9 15 10 12 13 14]]
fmt.Println(re.FindAllStringSubmatchIndex("-axxxbyc-axxbyc-axbyc-", 1)) //[[1 8 2 5 6 7]]
fmt.Println(re.FindAllStringSubmatchIndex("-abzc-abzc-", -1)) //[[1 5 2 2 3 4] [6 10 7 7 8 9]]
fmt.Println(re.FindAllStringSubmatchIndex("-abzc-abzc-", 1)) //[[1 5 2 2 3 4]]
fmt.Println(re.FindAllStringSubmatchIndex("-aczc-", -1)) //[],整个都不匹配,更没有分组匹配,将返回空数组

替换操作 ———— 即前缀带有Replace的函数

1
2
3
4
5
6
7
8
9
10
11
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte        // 返回src的一个拷贝,将src中所有re的匹配结果都替换为repl。repl参数被直接使用,不会使用Expand进行扩展。
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string // 返回src的一个拷贝,将src中所有re的匹配结果都替换为repl。repl参数被直接使用,不会使用Expand进行扩展。


// 下面两个函数和上面的区别在与当使用$时,不会再将其当作一个简单的字符,而是进行规则替换。**了解规则可去看下面的Expand函数
func (re *Regexp) ReplaceAll(src, repl []byte) []byte // 返回src的一个拷贝,将src中所有re的匹配结果都替换为repl。在替换时,repl中的'$'符号会按照Expand方法的规则进行解释和替换,例如$1会被替换为第一个分组匹配结果。
func (re *Regexp) ReplaceAllString(src, repl string) string // 返回src的一个拷贝,将src中所有re的匹配结果都替换为repl。在替换时,repl中的'$'符号会按照Expand方法的规则进行解释和替换,例如$1会被替换为第一个分组匹配结果。


func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte // 返回src的一个拷贝,将src中所有re的匹配结果(设为matched)都替换为repl(matched)。repl返回的切片被直接使用,不会使用Expand进行扩展。
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string // 返回src的一个拷贝,将src中所有re的匹配结果(设为matched)都替换为repl(matched)。repl返回的字符串被直接使用,不会使用Expand进行扩展。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
re := regexp.MustCompile("a(x*)b") //进行最左最长匹配
fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T")) // -T-T-
fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1")) // -$1-$1-
fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}")) // -${1}-${1}-


re := regexp.MustCompile("a(x*)b")
fmt.Println(re.ReplaceAllString("-ab-axxb-", "T")) //-T-T-

//这里$1表示的是每一个匹配的第一个分组匹配结果
//这里第一个匹配的第一个分组匹配为空,即将匹配的ab换为空值;
//第二个匹配的第一个分组匹配为xx,即将匹配的axxb换为xx
fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1")) //--xx-
fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}w"))//-w-xxw-

//因为这个例子每个匹配的第二个分组匹配都为空,所以将所有匹配字符串都替换成了空值
fmt.Println(re.ReplaceAllString("-ab-axxb-", "${2}"))//---

前缀为Expand的函数

1
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte

Expand返回新生成的将template添加到dst后面的切片。在添加时,Expand会将template中的变量替换为从src匹配的结果。match应该是被FindSubmatchIndex返回的匹配结果起止位置索引。(通常就是匹配src,除非你要将匹配得到的位置用于另一个[]byte)

在template参数里,一个变量表示为格式如:$name或${name}的字符串,其中name是长度>0的字母、数字和下划线的序列。一个单纯的数字字符名如$1会作为捕获分组的数字索引;其他的名字对应(?P…)语法产生的命名捕获分组的名字,即可以在使用Compile解析正则表达式的时候使用(?P…)语法命名匹配到的内容,然后之后能够使用$name或${name}表示匹配到的值。

超出范围的数字索引、索引对应的分组未匹配到文本、正则表达式中未出现的分组名,都会被替换为空切片。

$name格式的变量名,name会尽可能取最长序列:$1x等价于${1x}而非${1}x,$10等价于${10}而非${1}0。因此$name适用在后跟空格/换行等字符的情况,${name}适用所有情况。

如果要在输出中插入一个字面值’$’,在template里可以使用$$。

1
func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte

ExpandString类似Expand,但template和src参数为字符串。它将替换结果添加到切片并返回切片,以便让调用代码控制内存申请。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main 
import(
"fmt"
"regexp"

)

func main() {
src := []byte(`
call hello alice
hello bob
call hello eve
`)
pat := regexp.MustCompile(`(?m)(call)\s+(?P<cmd>\w+)\s+(?P<arg>.+)\s*$`)
res := []byte{}
for _, s := range pat.FindAllSubmatchIndex(src, -1){
res = pat.Expand(res, []byte("$cmd('$arg')\n"), src, s)
}
fmt.Println(string(res))
}

返回:

1
2
3
4
5
userdeMacBook-Pro:go-learning user$ go run test.go
hello('alice') //
hello('eve')

userdeMacBook-Pro:go-learning user$

上面例子使用到的正则表达式语法为

分组:

1
2
3
4
5
(re)           编号的捕获分组
(?P<name>re) 命名并编号的捕获分组
(?:re) 不捕获的分组
(?flags) 设置当前所在分组的标志,不捕获也不匹配
(?flags:re) 设置re段的标志,不捕获的分组

flags的语法为xyz(设置)、-xyz(清楚)、xy-z(设置xy,清楚z),标志如下:

1
2
3
4
I              大小写敏感(默认关闭)
m ^和$在匹配文本开始和结尾之外,还可以匹配行首和行尾(默认开启)
s 让.可以匹配\n(默认关闭)
U 非贪婪的:交换x*和x*?、x+和x+?……的含义(默认关闭)

因此

  • (?m) 表示行首
  • (?P<cmd>\w+) 表示将符合 \w+(\w== [0-9A-Za-z_]) 正则表达式的值命名为name,之后再使用函数时可以使用 $name 来表达

其他函数

func (*Regexp) Split

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

Split将re在s中匹配到的结果作为分隔符将s分割成多个字符串,并返回这些正则匹配结果之间的字符串的切片。

返回的切片不会包含正则匹配的结果,只包含匹配结果之间的片段。当正则表达式re中不含正则元字符时,本方法等价于strings.SplitN。

参数n绝对返回的子字符串的数量:

1
2
3
n > 0 : 返回最多n个子字符串,最后一个子字符串是剩余未进行分割的部分。
n == 0: 返回nil (zero substrings)
n < 0 : 返回所有子字符串

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main 
import(
"fmt"
"regexp"

)

func main() {
s1 := regexp.MustCompile("a*").Split("abaabaccadaaae", -1) //返回所有值
fmt.Println(s1) //[ b b c c d e]
s2 := regexp.MustCompile("a*").Split("abaabaccadaaae", 5) //返回5个值,剩余的值都写在最后一个值中
fmt.Println(s2) //[ b b c cadaaae]
}

func QuoteMeta

1
func QuoteMeta(s string) string

QuoteMeta返回将s中所有正则表达式元字符都进行转义后字符串。该字符串可以用在正则表达式中匹配字面值s。例如,QuoteMeta(`[foo]`)会返回`\[foo\]`。

特殊字符有:\.+*?()|[]{}^$ ,`这些字符用于实现正则语法,所以当作普通字符使用时需要转换`

func (*Regexp) String

1
func (re *Regexp) String() string

String返回用于编译成正则表达式的字符串

func (*Regexp) NumSubexp

1
func (re *Regexp) NumSubexp() int

NumSubexp返回该正则表达式中捕获分组的数量。

func (*Regexp) SubexpNames

1
func (re *Regexp) SubexpNames() []string

SubexpNames返回该正则表达式中捕获分组的名字。第一个分组的名字是names[1],因此,如果m是一个组匹配切片,m[i]的名字是SubexpNames()[i]。因为整个正则表达式是无法被命名的,names[0]必然是空字符串。该切片不应被修改。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main 
import(
"fmt"
"regexp"

)

func main() {
re := regexp.MustCompile("(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)")
fmt.Println(re.String()) //(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)
fmt.Println(re.NumSubexp()) //2

fmt.Println(re.MatchString("Alan Turing")) //true
fmt.Printf("%q\n", re.SubexpNames()) //["" "first" "last"],分组从索引[1]开始,[0]为空字符串
reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1])
fmt.Println(reversed) //${last} ${first}
fmt.Println(re.ReplaceAllString("Alan Turing", reversed)) //Turing Alan,实现前后转换
}

func (*Regexp) LiteralPrefix -返回所有匹配项都共同拥有的前缀(去除可变元素)

1
func (re *Regexp) LiteralPrefix() (prefix string, complete bool)

LiteralPrefix返回一个字符串字面值prefix,任何匹配本正则表达式的字符串都会以prefix起始。如果该字符串字面值包含整个正则表达式,返回值complete会设为真。

complete:如果 prefix 就是正则表达式本身,则返回 true,否则返回 false

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main 
import(
"fmt"
"regexp"

)

func main() {
reg := regexp.MustCompile("(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)")
prefix, complete := reg.LiteralPrefix()
fmt.Println(prefix) //匹配项为空,因为 从头开始就是可变元素
fmt.Println(complete)

reg = regexp.MustCompile(`Hello[\w\s]+`)
fmt.Println(reg.LiteralPrefix()) //任何字符串要匹配该正则表达式时其必须有前缀Hello,因为前缀不是整个正则,所以为false
// Hello false
reg = regexp.MustCompile(`Hello`)//因为前缀为整个正则,所以返回true
fmt.Println(reg.LiteralPrefix())
// Hello true
}

Reference


Go 标准库之 regexp
https://flepeng.github.io/021-Go-32-Go-标准库-Go-标准库之-regexp/
作者
Lepeng
发布于
2024年12月3日
许可协议