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 正则表达式的语法、规则、常用函数以及最佳实践,希望能够帮助您更好地理解和使用正则表达式,提高开发效率。记住,实践是掌握正则表达式的关键,多写多练才能熟能生巧。