高级

分组

有时你想匹配一段文本,但只想提取出部分的内容。 在之前的正则表达式基础中,我们都是匹配整个文本


re, err := regexp.Compile(`.at`)
res := re.FindAllStringSubmatch("The cat sat on the mat.", -1)
fmt.Printf("%v", res) //[[cat] [sat] [mat]]

() 允许捕获分字符块,而不是整个表达式

re, err := regexp.Compile(`(.)at`) // want to know what is in front of 'at'
res := re.FindAllStringSubmatch("The cat sat on the mat.", -1)
fmt.Printf("%v", res)  //[[cat c] [sat s] [mat m]]

你也可以指定多个()分组

// Prints [[ex e x] [ec e c] [e  e  ]]
s := "Nobody expects the Spanish inquisition."
re1, err := regexp.Compile(`(e)(.)`) // Prepare our regex
result_slice := re1.FindAllStringSubmatch(s, -1)
fmt.Printf("%v", result_slice)

FindAllStringSubmatch()函数, 对于每一次匹配, 返回一个数组,第一字段是整个匹配到的字符串,之后的则是()中的分组内容。
如果你有一个可选的分组,而它又没有出现在字符串中, 则返回的数组会在此位置有一个空字符串, 换句语说,返回的数组长度等于正则表达式中()的数量加1。

s := "Mr. Leonard Spock"
re1, err := regexp.Compile(`(Mr)(s)?\. (\w+) (\w+)`)
result:= re1.FindStringSubmatch(s)

for k, v := range result {
    fmt.Printf("%d. %s\n", k, v)
}
// Prints
// 0. Mr. Leonard Spock
// 1. Mr
// 2.
// 3. Leonard
// 4. Spock

对匹配项进行命名

依次将匹配项保存到一个数组中,会出现以下两个问题 。

  • 当你要正则表达式插入一个新的分组. 在此之后的index,都会加1。
  • 字符串可能是在运行时构造,可能包含多个()
re := regexp.MustCompile("(?P<first_char>.)(?P<middle_part>.*)(?P<last_char>.)")
n1 := re.SubexpNames()
r2 := re.FindAllStringSubmatch("Super", -1)[0]

fmt.Println(n1) 
fmt.Println(r2)

md := map[string]string{}
for i, n := range r2 {
    fmt.Printf("%d. match='%s'\tname='%s'\n", i, n, n1[i])
    md[n1[i]] = n
}
fmt.Printf("The names are  : %v\n", n1)
fmt.Printf("The matches are: %v\n", r2)
fmt.Printf("The first character is %s\n", md["first_char"])
fmt.Printf("The last  character is %s\n", md["last_char"])

Output

[ first_char middle_part last_char]  //index 0 为 ''
[Super S upe r]
0. match='Super'    name=''
1. match='S'    name='first_char'
2. match='upe'  name='middle_part'
3. match='r'    name='last_char'
The names are  : [ first_char middle_part last_char]
The matches are: [Super S upe r]
The first character is S
The last  character is r

高级重复

非捕获重复

如果()只是用来分组,进行重复,而不想被捕获。 可以使用(?:regex)。 以下是捕获所用分组的用法

s := "Mrs. Leonora Spock"
re1, err := regexp.Compile(`Mr(s)?\. (\w+) (\w+)`)
result:= re1.FindStringSubmatch(s)
for k, v := range result {
    fmt.Printf("%d. %s\n", k, v)
}
// 0. Mrs. Leonora Spock
// 1. s
// 2. Leonora
// 3. Spock

非捕获分组

s := "Mrs. Leonora Spock"
re1, err := regexp.Compile(`Mr(?:s)?\. (\w+) (\w+)`)
result:= re1.FindStringSubmatch(s)
for k, v := range result {
    fmt.Printf("%d. %s\n", k, v)
}
// 0. Mrs. Leonora Spock
// 1. Leonora
// 2. Spock

指定重复次数

s := "11110010101111100101001001110101"
re1, err := regexp.Compile(`1{4}`)
res := re1.FindAllStringSubmatch(s,-1)
fmt.Printf("<%v>", res)
// <[[1111] [1111]]>

res2 := re1.FindAllStringIndex(s,-1)
fmt.Printf("<%v>", res2)
// <[[0 4] [10 14]]>

{}的语法很少使用。一个原因是你可以用一种简单的方式来重写表达式。

(ab){3} == (ababab)
(ab){3,4} == (ababab(ab)??)

??表示0个或者1个,但更喜欢0个

标志

正则表达式可以使用以下标志

  • i 忽略大小写(默认false)
  • m 多行模式:^ 和 $ 匹配每行的begin/end,而不是整个文本的begin/end (default false)
  • s 让 .匹配\n (default false)
  • U 非贪婪模式: x* -> x*?, x+ -> x+? (default false)

忽略大小写

s := "Never say never."
r, err := regexp.Compile(`(?i)^n`)     // Do we have an 'N' or 'n' at the beginning?
fmt.Printf("%v", r.MatchString(s)) // true, case insensitive

在正则的开发中,很少使用到正则表达式的忽略大小写匹配。 通常我们是将整个表达式统一为大小或者小写之后在进行匹配

sMixed := "Never say never."
sLower := strings.ToLower(sMixed) // don't forget to import "strings"
r, err := regexp.Compile(`^n`)
fmt.Printf("%v ", r.MatchString(sMixed))  // false, N != n
fmt.Printf("%v ", r.MatchString(sLower))  // true,  n == n

贪婪与非贪婪

r, err := regexp.Compile(`'.*'`)
res := r.FindString(" 'abc','def','ghi' ")
fmt.Printf("<%v>", res)
// Will print: <'abc','def','ghi'>

你可以使用非贪婪模式,只匹配'abc'

r, err := regexp.Compile(`'.*?'`)
res := r.FindString(" 'abc','def','ghi' ")
fmt.Printf("<%v>", res)
// Will print: <'abc'>

你也可以使用U flag

r, err := regexp.Compile(`(?U)'.*'`)
res := r.FindString(" 'abc','def','ghi' ")
fmt.Printf("<%v>", res)
// Will print: <'abc'>

Dot(.) 可以匹配\n

当你有一个多行的字符串(一个字符串中包含'\n')。你可以使用(?s) flag, 让 '.'可以匹配\n. 默认为false,如下

r, err := regexp.Compile(`a.`)
s := "atlanta\narkansas\nalabama\narachnophobia"
res := r.FindAllString(s, -1)
fmt.Printf("<%v>", res)
// <[at an ar an as al ab am ar ac]>

使用(?s) flag

r, err := regexp.Compile(`(?s)a.`)
s := "atlanta\narkansas\nalabama\narachnophobia"
res := r.FindAllString(s, -1)
fmt.Printf("<%v>", res)
// Prints
// <[at an a
// ar an as al ab am a
// ar ac]>

^/$ 匹配新行

r, err1 := regexp.Compile(`a$`) // without flag
s := "atlanta\narkansas\nalabama\narachnophobia"
//    01234567 890123456 78901234 5678901234567
//                                            -
res := r.FindAllStringIndex(s,-1)
fmt.Printf("<%v>\n", res)
// 1 match
// <[[37 38]]>

t, err2 := regexp.Compile(`(?m)a$`) // with flag
u := "atlanta\narkansas\nalabama\narachnophobia"
//    01234567 890123456 78901234 5678901234567
//          --                 --             -
res2 := t.FindAllStringIndex(u,-1)
fmt.Printf("<%v>", res2)
// 3 matches
// <[[6 7] [23 24] [37 38]]>