Golang正则表达式匹配规则:速查手册 (方便快速参考)

Golang 正则表达式匹配规则:速查手册(3000 字详细版)

Golang 的 regexp 包提供了强大的正则表达式功能,支持各种复杂的文本匹配和处理需求。本文将深入探讨 Golang 正则表达式的语法、规则、常用函数以及最佳实践,旨在为您提供一份详尽且易于查阅的参考手册。

1. 基础概念

正则表达式是一种用于描述文本模式的特殊字符串。它由普通字符(例如字母、数字、标点符号)和元字符(具有特殊含义的字符)组成。正则表达式引擎会根据这些模式在文本中进行搜索、匹配和替换操作。

1.1. Golang 中的正则表达式

Golang 的 regexp 包实现了 RE2 引擎,这是一个高效且安全的正则表达式引擎,避免了传统引擎可能存在的安全漏洞(例如回溯陷阱)。

1.2. 正则表达式的组成

一个正则表达式通常由以下部分组成:

  • 普通字符: 与自身匹配的字符,例如 a, 1, !.
  • 元字符: 具有特殊含义的字符,用于构建更复杂的模式。
  • 量词: 指定字符或分组出现的次数。
  • 字符类: 匹配一组字符中的任意一个。
  • 锚点: 匹配字符串的特定位置(例如开头、结尾)。
  • 分组和捕获: 将表达式的一部分组合在一起,并可选择捕获匹配的文本。
  • 修饰符/标志: 修改正则表达式的行为(例如忽略大小写)。
  • 转义字符: 使用反斜杠 \ 来转义元字符,使其匹配自身。

2. 元字符详解

下表列出了 Golang regexp 包中常用的元字符及其含义:

元字符 含义 示例
. 匹配除换行符 (\n) 以外的任意单个字符。 a.c 匹配 "abc", "a1c", "a#c" 等
^ 匹配字符串的开头,或行的开头(多行模式下)。 ^abc 匹配以 "abc" 开头的字符串
$ 匹配字符串的结尾,或行的结尾(多行模式下)。 xyz$ 匹配以 "xyz" 结尾的字符串
* 匹配前面的字符或分组零次或多次(贪婪模式)。 ab*c 匹配 "ac", "abc", "abbc" 等
+ 匹配前面的字符或分组一次或多次(贪婪模式)。 ab+c 匹配 "abc", "abbc", "abbbc"等
? 匹配前面的字符或分组零次或一次(贪婪模式)。 ab?c 匹配 "ac" 或 "abc"
{n} 匹配前面的字符或分组恰好 n 次。 a{3} 匹配 "aaa"
{n,} 匹配前面的字符或分组至少 n 次。 a{2,} 匹配 "aa", "aaa", "aaaa" 等
{n,m} 匹配前面的字符或分组至少 n 次,但不超过 m 次。 a{2,4} 匹配 "aa", "aaa", "aaaa"
[] 字符类,匹配方括号内的任意一个字符。 [abc] 匹配 "a", "b" 或 "c"
[^] 否定字符类,匹配除方括号内字符以外的任意一个字符。 [^abc] 匹配除 "a", "b", "c" 外的字符
[a-z] 字符范围,匹配指定范围内的任意一个字符。 [a-z] 匹配任意小写字母
| 或运算符,匹配左侧或右侧的表达式。 cat\|dog 匹配 "cat" 或 "dog"
() 分组,将表达式的一部分组合在一起。 (ab)+ 匹配 "ab", "abab", "ababab"等
\d 匹配任意一个数字,等价于 [0-9] \d+ 匹配一个或多个数字
\D 匹配任意一个非数字字符,等价于 [^0-9] \D+ 匹配一个或多个非数字字符
\w 匹配任意一个字母、数字或下划线,等价于 [a-zA-Z0-9_] \w+ 匹配一个或多个单词字符
\W 匹配任意一个非字母、数字或下划线字符,等价于 [^a-zA-Z0-9_] \W+ 匹配一个或多个非单词字符
\s 匹配任意一个空白字符(空格、制表符、换行符等)。 \s+ 匹配一个或多个空白字符
\S 匹配任意一个非空白字符。 \S+ 匹配一个或多个非空白字符
\b 匹配单词边界(单词字符和非单词字符之间的位置)。 \bword\b 匹配整个单词 "word"
\B 匹配非单词边界。
\ 转义字符,用于转义后面的元字符,让其匹配自身. \. 匹配 .

2.1. 量词的贪婪与非贪婪模式

默认情况下,量词(*, +, ?, {n,}, {n,m})是贪婪的,它们会尽可能多地匹配字符。在量词后面加上 ? 可以将其变为非贪婪模式,使其尽可能少地匹配字符。

  • 贪婪模式: a.*b 在 "a1b2b3b" 中匹配 "a1b2b3b"。
  • 非贪婪模式: a.*?b 在 "a1b2b3b" 中匹配 "a1b"。

3. 字符类

字符类用于匹配一组字符中的任意一个。

  • [abc]: 匹配 "a", "b" 或 "c"。
  • [a-z]: 匹配任意小写字母。
  • [A-Z]: 匹配任意大写字母。
  • [0-9]: 匹配任意数字。
  • [a-zA-Z0-9]: 匹配任意字母或数字。
  • [^abc]: 匹配除 "a", "b", "c" 以外的任意字符。
  • [\d\s]: 匹配任意一个数字或者空白字符

4. 分组和捕获

分组使用圆括号 () 将表达式的一部分组合在一起。捕获组会将匹配的文本保存起来,以便后续引用。

  • (ab)+: 匹配一个或多个 "ab"。
  • (.*?) (.*?): 匹配两个由空格分隔的任意字符串,并分别捕获它们。

4.1. 非捕获组

使用 (?:...) 可以创建一个非捕获组,它只用于分组,不捕获匹配的文本。这在不需要捕获文本时可以提高效率。

  • (?:ab)+: 匹配一个或多个 "ab",但不捕获匹配的文本。

4.2. 命名捕获组

使用(?P<name>...)可以创建命名捕获组,可以使用名称来引用.

  • (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}): 匹配日期,并分别用名称year,month,day捕获.

5. 锚点

锚点用于匹配字符串的特定位置。

  • ^: 匹配字符串的开头。
  • $: 匹配字符串的结尾。
  • \A: 匹配字符串的开头(忽略多行模式)。
  • \z: 匹配字符串的结尾(忽略多行模式)。
  • \b: 匹配单词边界。
  • \B: 匹配非单词边界。

6. 修饰符/标志

修饰符(也称为标志)用于修改正则表达式的行为。在 Golang 中,修饰符通常以内联方式使用,放在正则表达式的开头,例如 (?i)abc

  • (?i): 忽略大小写。
  • (?m): 多行模式,使 ^$ 匹配每一行的开头和结尾。
  • (?s): 单行模式,使 . 匹配包括换行符在内的任意字符。
  • (?U): 交换贪婪和非贪婪的含义(不常用)。

示例:

  • (?i)abc 匹配 "abc", "Abc", "aBc", "ABC" 等。
  • (?m)^abc$ 匹配多行文本中以 "abc" 开头和结尾的行。
  • (?s)a.c 匹配 "a\nc"。

7. Golang regexp 包常用函数

regexp 包提供了以下常用函数:

  • Compile(expr string) (*Regexp, error): 编译正则表达式,返回一个 Regexp 对象。如果正则表达式有语法错误,会返回错误。
  • MustCompile(expr string) *Regexp:Compile 类似,但如果正则表达式有语法错误,会 panic。
  • Match(pattern string, b []byte) (matched bool, err error): 检查字节切片 b 是否包含正则表达式 pattern 的任何匹配项。
  • MatchString(pattern string, s string) (matched bool, err error): 检查字符串 s 是否包含正则表达式 pattern 的任何匹配项。
  • Find(b []byte) []byte: 返回字节切片 b 中最左侧的匹配项的字节切片。
  • FindString(s string) string: 返回字符串 s 中最左侧的匹配项的字符串。
  • FindAll(b []byte, n int) [][]byte: 返回字节切片 b 中所有不重叠的匹配项的字节切片。n 指定最大匹配次数,如果 n 为 -1,则返回所有匹配项。
  • FindAllString(s string, n int) []string: 返回字符串 s 中所有不重叠的匹配项的字符串。n 指定最大匹配次数,如果 n 为 -1,则返回所有匹配项。
  • FindIndex(b []byte) (loc []int): 返回字节切片 b 中最左侧的匹配项的起始和结束索引。
  • FindStringIndex(s string) (loc []int): 返回字符串 s 中最左侧的匹配项的起始和结束索引。
  • FindAllIndex(b []byte, n int) [][]int: 返回字节切片 b 中所有不重叠的匹配项的起始和结束索引。
  • FindAllStringIndex(s string, n int) [][]int:返回字符串 s 中所有不重叠的匹配项的起始和结束索引.
  • FindSubmatch(b []byte) [][]byte: 返回一个包含匹配项和所有捕获组的字节切片。第一个元素是整个匹配项,后续元素是捕获组。
  • FindStringSubmatch(s string) []string: 返回一个包含匹配项和所有捕获组的字符串切片。第一个元素是整个匹配项,后续元素是捕获组。
  • FindAllSubmatch(b []byte, n int) [][][]byte: 返回一个包含所有不重叠匹配项及其捕获组的字节切片。
  • FindAllStringSubmatch(s string, n int) [][]string: 返回一个包含所有不重叠匹配项及其捕获组的字符串切片。
  • ReplaceAll(b []byte, repl []byte) []byte: 将字节切片 b 中所有匹配项替换为 repl
  • ReplaceAllString(s string, repl string) string: 将字符串 s 中所有匹配项替换为 repl
  • ReplaceAllFunc(b []byte, repl func([]byte) []byte) []byte: 将字节切片 b 中所有匹配项使用 repl 函数进行替换。
  • ReplaceAllStringFunc(s string, repl func(string) string) string: 将字符串 s 中所有匹配项使用 repl 函数进行替换。
  • Split(s string, n int) []string:使用正则表达式作为分隔符来分割字符串. n 控制分割的次数。

示例:

```go
package main

import (
"fmt"
"regexp"
)

func main() {
// 编译正则表达式
re := regexp.MustCompile(a(b+)c)

// 匹配字符串
s := "abc abbc abbbc"
fmt.Println(re.MatchString(s)) // true

// 查找匹配项
fmt.Println(re.FindString(s)) // abc

// 查找所有匹配项
fmt.Println(re.FindAllString(s, -1)) // [abc abbc abbbc]

// 查找子匹配项 (捕获组)
fmt.Println(re.FindStringSubmatch(s)) // [abc bb]

// 查找所有匹配项的索引
fmt.Println(re.FindAllStringIndex("abc abbc acc",-1)) //[[0 3] [4 8] [9 12]]

// 替换匹配项
fmt.Println(re.ReplaceAllString(s, "x")) // x x x

//使用命名捕获组
re2 := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
match := re2.FindStringSubmatch("2023-10-27")
result := make(map[string]string)
for i, name := range re2.SubexpNames() {
    if i != 0 && name != "" {
        result[name] = match[i]
    }
}
fmt.Println(result) // map[day:27 month:10 year:2023]

//使用Split分割
re3 := regexp.MustCompile(`\s*;\s*`)
fmt.Println(re3.Split("a;b; c ; d", -1)) // [a b c d]

}
```

8. 最佳实践

  • 优先使用 MustCompile: 在程序初始化阶段使用 MustCompile 编译正则表达式,避免在每次使用时重复编译,提高效率。
  • 使用非捕获组: 如果不需要捕获匹配的文本,使用非捕获组 (?:...) 可以提高效率。
  • 避免复杂的正则表达式: 过于复杂的正则表达式难以理解和维护,尽量将其拆分为多个简单的正则表达式。
  • 注意贪婪与非贪婪模式: 根据需要选择合适的模式,避免意外的匹配结果。
  • 测试正则表达式: 使用不同的输入数据测试正则表达式,确保其行为符合预期。
  • 使用QuoteMeta转义字符串:如果需要匹配用户输入的文本,可以使用 regexp.QuoteMeta 函数将字符串中的所有元字符转义,避免意外的正则表达式行为。
  • 了解RE2引擎的限制: Golang的regexp包使用的是RE2引擎,它不支持一些Perl兼容正则表达式(PCRE)的特性,如后向引用,零宽断言。

9. 总结

Golang 的 regexp 包提供了强大而灵活的正则表达式功能,可以满足各种文本处理需求。本文详细介绍了 Golang 正则表达式的语法、规则、常用函数以及最佳实践,希望能够帮助您更好地理解和使用正则表达式,提高开发效率。记住,实践是掌握正则表达式的关键,多写多练才能熟能生巧。

THE END