原文链接:https://xiets.blog.csdn.net/article/details/130866749
版权声明:原创文章禁止转载
专栏目录:Golang 专栏(总目录)
Go 语言 正则表达式 处理使用内置的 regexp
模块。
regexp
包内主要的类型和函数:
// 表示一个预编译的正则表达式
type Regexp struct {
// 没有导出属性
}
// 判断给定的 []byte 是否是否包含匹配 pattern 正则表达式的子文本
func Match(pattern string, b []byte) (matched bool, err error)
// 从 reader 中读取 []byte, 然后再判断是否匹配 pattern
func MatchReader(pattern string, r io.RuneReader) (matched bool, err error)
// 判断给定的 string 是否是否包含匹配 pattern 正则表达式的子字符串
func MatchString(pattern string, s string) (matched bool, err error)
// 把指定字符串中的所有 正则表达式元字符 加上转义符号并返回, 返回的字符串是与原文本匹配的正则表达式。
func QuoteMeta(s string) string
// 编译正则表达式, 返回 正则表达式对象 和 错误。
func Compile(expr string) (*Regexp, error)
// 类似于 Compile(), 但如果无法解析表达式, 则会引发宕机。它简化了保存已编译正则表达式作为全局变量的安全初始化。
func MustCompile(str string) *Regexp
// 类似于 Compile(), 但将正则表达式限制为 POSIX ERE (egrep) 语法并将匹配语义更改为最左最长。
func CompilePOSIX(expr string) (*Regexp, error)
// 类似于 CompilePOSIX(), 但如果无法解析表达式, 则会引发宕机。它简化了保存已编译正则表达式作为全局变量的安全初始化。
func MustCompilePOSIX(str string) *Regexp
1. 匹配字符串
判断文本字符串是否匹配指定正则表达式规则:
package main
import (
"bytes"
"fmt"
"regexp"
)
func main() {
matched, err := regexp.Match("^[0-9]+$", []byte("012345"))
fmt.Println(matched, err) // true <nil>
matched, err = regexp.Match("^[1-9][0-9]+$", []byte("012345"))
fmt.Println(matched, err) // false <nil>
matched, err = regexp.MatchString("^[1-9]+.*", "123456abc")
fmt.Println(matched, err) // true <nil>
matched, err = regexp.MatchString("abc", "123abc456")
fmt.Println(matched, err) // true <nil>
r := bytes.NewReader([]byte("abc"))
matched, err = regexp.MatchReader("^[a-z]+$", r)
fmt.Println(matched, err) // true <nil>
s := "^[a-z]+ab(c)?.*$"
quotePattern := regexp.QuoteMeta(s)
fmt.Println(quotePattern) // \^\[a-z\]\+ab\(c\)\?\.\*\$
matched, err = regexp.MatchString(quotePattern, s)
fmt.Println(matched, err) // true <nil>
// 以上快速判断匹配的方法内部也是先编译正则表达式再匹配
}
2. 正则表达式对象: regexp.Regexp
regexp.Regexp 表示一个正则表达式对象。
regexp.Regexp
的 匹配方法:
// 判断指定 文本/字符串 中是否有匹配 正则表达式(re) 的 子文本/字符串
func (re *Regexp) Match(b []byte) bool
func (re *Regexp) MatchReader(r io.RuneReader) bool
func (re *Regexp) MatchString(s string) bool
regexp.Regexp
的 查找方法:
// 从指定 文本/字符串 中查找匹配 re 的第一个(最左侧) 子文本/字符串, 没有找到返回零值
func (re *Regexp) Find(b []byte) []byte
func (re *Regexp) FindString(s string) string
// 从指定 文本/字符串 中查找匹配 re 的第一个(最左侧) 子文本/字符串 的首尾索引位置, 没有找到返回零值
func (re *Regexp) FindIndex(b []byte) (loc []int)
func (re *Regexp) FindStringIndex(s string) (loc []int)
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
// 从指定 文本/字符串 中查找匹配 re 的所有 子文本/字符串,
// n 表示最多查找的数量, n < 0 表示查找所有, 没有找到返回零值
func (re *Regexp) FindAll(b []byte, n int) [][]byte
func (re *Regexp) FindAllString(s string, n int) []string
// 从指定 文本/字符串 中查找匹配 re 的所有 子文本/字符串 的首尾索引位置
// n 表示最多查找的数量, n < 0 表示查找所有, 没有找到返回零值
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
// 从指定 文本/字符串 中查找匹配 re 的第一个(最左侧) 子文本/字符串,
// 包括 捕获组 匹配的子项。没有找到返回零值。
func (re *Regexp) FindSubmatch(b []byte) [][]byte
func (re *Regexp) FindStringSubmatch(s string) []string
// 从指定 文本/字符串 中查找匹配 re 的第一个(最左侧) 子文本/字符串 的首尾索引位置,
// 包括 捕获组 匹配的子项。没有找到返回零值。
func (re *Regexp) FindSubmatchIndex(b []byte) []int
func (re *Regexp) FindStringSubmatchIndex(s string) []int
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
// 从指定 文本/字符串 中查找匹配 re 的所有 子文本/字符串, 没有找到返回零值,
// 包括 捕获组 匹配的子项。没有找到返回零值。
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
// 从指定 文本/字符串 中查找匹配 re 的所有 子文本/字符串 的首尾索引位置,
// 包括 捕获组 匹配的子项。没有找到返回零值。
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
regexp.Regexp
的 替换方法:
// 把 src 中所有匹配 re 的子文本/字符串替换为 relp, 返回副本。
// 在 repl 中, $ 被解释为在 Expand 中的符号, 因此例如 ${1} 代表编号为1的捕获组。
func (re *Regexp) ReplaceAll(src, repl []byte) []byte
func (re *Regexp) ReplaceAllString(src, repl string) string
// 把 src 中所有匹配 re 的子文本/字符串替换为 relp, 返回副本。不使用 Expand。
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string
// 把 src 中所有匹配 re 的子文本/字符串 调用 relp() 函数并替换为其返回值, 返回副本。不使用 Expand。
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string
regexp.Regexp
的 其他方法:
// 使用 re 匹配的子字符串作为分隔符拆分 字符串s, 并返回拆分后的 子字符串切片。
// 没有匹配的子字符串, 整个原字符串作为子字符串, 即返回 []string{s}。
// n > 0: 最多 n 个字符串, 最后一个子字符串将是未拆分的余数
// n == 0: 结果为 nil(零个子字符串)
// n < 0: 所有子字符串
func (re *Regexp) Split(s string, n int) []string
// 调用后未来的搜索匹配按最长匹配
func (re *Regexp) Longest()
// 返回正则表达式的原字符串表示
func (re *Regexp) String() string
// Other ...
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte
func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte
func (re *Regexp) SubexpIndex(name string) int
func (re *Regexp) SubexpNames() []string
func (re *Regexp) NumSubexp() int
func (re *Regexp) LiteralPrefix() (prefix string, complete bool)
regexp.Regexp
代码示例:
package main
import (
"fmt"
"regexp"
)
func main() {
// 匹配
re := regexp.MustCompile("a[0-9]+b")
fmt.Println(re.MatchString("123a0b456")) // true
// 查找
re = regexp.MustCompile("[a-z]+")
fmt.Println(re.FindString("12ab34cd56xyz")) // ab
fmt.Println(re.FindAllString("12ab34cd56xyz", -1)) // [ab cd xyz]
fmt.Println(re.FindStringIndex("12ab34cd56xyz")) // [2 4]
// 替换
re = regexp.MustCompile("[a-z]+")
fmt.Println(re.ReplaceAllString("12ab34cd56xyz", "-")) // 12-34-56-
re = regexp.MustCompile("a([xy]*)b")
fmt.Println(re.ReplaceAllString("-ab-axxb-axyb-", "A${1}B")) // -AB-AxxB-AxyB-
// 上面的匹配替换, 用正则表达式 "a([xy]*)b" 查找 "-ab-axxb-axyb-",
// 可以找出 3 个匹配子项: "ab"、"axxb" 和 "axyb"。
// 后面的替换字符串中的 ${1} 表示捕获组:
// 第 1 个匹配子项 "ab" 编号为 1 的捕获组为 "", 则替换的内容为 "AB";
// 第 2 个匹配子项 "axxb" 编号为 1 的捕获组为 "xx", 则替换的内容为 "AxxB";
// 第 3 个匹配子项 "axyb" 编号为 1 的捕获组为 "xy", 则替换的内容为 "AxyB";
// 也可以对捕获组命名保存, 使用时使用名称代替编号
re = regexp.MustCompile("a(?P<Hi>[xy]*)b")
fmt.Println(re.ReplaceAllString("-ab-axxb-axyb-", "A${Hi}B")) // -AB-AxxB-AxyB-
}
3. 正则表达式 语法
查看正则表达式语法,运行命令:go doc regexp/syntax
其他语言正则表达式文档:
字符:
x 字符x
\\ 反斜杠字符 "\"
\0nn 八进制值 0nn 表示的字符 (0 <= n <= 7)
\xhh 十六禁止 0xhh 表示的字符
\uhhhh Unicode 表示的字符
\t 制表符 ('\u0009')
\n 换行符 ('\u000A')
\r 回车符 ('\u000D')
\f 换页符 ('\u000C')
\a 报警符 ('\u0007')
\e 转义符 ('\u001B')
\x 把正则表达式的元字符 x 转义为普通字符 x
字符类:
[abc] 字符 a、b 或 c (简单类)
[^abc] 除了 a、b、c 以外的 任意字符 (否定)
[0-9] 0 到 9 范围内的字符 (范围)
[a-zA-Z] a 到 z 或 A 到 Z 范围内的字符 (并集)
[^a-z] 除了 a 到 z 以外的任意字符 (否定+并集)
预定义字符类:
. 任意字符 (不包括换行符, 如果要无差别匹配任意字符串可以使用 [\s\S] )
\d 数字: [0-9]
\D 非数字: [^0-9]
\s 空白字符: [ \t\n\x0B\f\r]
\S 非空白字符: [^\s]
\w 单词字符: [a-zA-Z_0-9]
\W 非单词字符: [^\w]
边界匹配器:
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符 (如果有)
\z 输入的结尾
数量词:
x* x, 0次 或 多次, 贪婪匹配 (最大匹配)
x+ x, 1次 或 多次, 贪婪匹配
x? x, 0次 或 1次, 贪婪匹配
x{n,m} x, n <= 次数 <= m, 贪婪匹配
x{n,} x, n <= 次数, 贪婪匹配
x{n} x, n == 次数
x*? x, 0次 或 多次, 非贪婪 (最小匹配)
x+? x, 1次 或 多次, 非贪婪
x?? x, 0次 或 1次, 非贪婪
x{n,m}? x, n <= 次数 <= m, 非贪婪
x{n,}? x, n <= 次数, 非贪婪
x{n}? x, n == 次数
逻辑运算符:
xy x 后面跟着 y
x|y x 或 y
分组:
(...) (捕获组) 分组匹配 ..., 对组内匹配到的子项进行编号保存 (从1开始编号, 整个正则表达式是一个编号为0的组)
(?P<name>...) (捕获组) 分组匹配 ..., 对组内匹配到的子项进行编号保存的同时再使用 name 作为名称保存
(?:...) (非捕获组) 分组匹配 ..., 不保存组内匹配到的子项
(?flags) (非捕获组) 设置当前组内的标记, 当前组内(此标记括号外所在组)有效
(?flags:...) (非捕获组) 设置 ... 期间的标记, ... 子表达式内有效
flags 标记是 'a', 'i', 'L', 'm', 's', 'u', 'x' 中的 0 或 多个:
'a' 只匹配ASCII字符
'i' 忽略大小写
'L' 语言依赖
'm' 多行模式
's' 点 dot 匹配全部字符
'u' Unicode 匹配
'x' 冗长模式
(?flags1-flags2:...) 还可以对已设置的标记去除,
表示当前组内设置标记 flags1 并去除标记 flags2
捕获组 与 非捕获组:
1. 捕获组会把分组匹配的组内子项结果保存起来,
调用诸如 FindSubmatch() 的包含 Submatch 关键字的查找方法会把捕获的组内子项结果一起返回。
2. 非捕获组则也是分组匹配, 但结果不保存。
3. 捕获组可以通过从左到右计算其开括号来编号, 例如表达式 ((A)(B(C))) 中, 存在 4 个捕获组:
1 ((A)(B(C)))
2 (A)
3 (B(C))
4 (C)
4. 整个正则表达式是一个编号为 0 的捕获组。
前瞻后视 (非捕获组):
re1(?=re2) 前瞻断言 (肯定 后面), 匹配 re1, 但后面必须是 re2 (结果子项不包含 re2)
re1(?!re2) 前瞻断言 (否定 后面), 匹配 re1, 但后面必须不是 re2 (结果子项不包含 re2)
(?<=re2)re1 后视断言 (肯定 前面), 匹配 re1, 但前面必须是 re2 (结果子项不包含 re2)
(?<!re2)re1 后视断言 (否定 前面), 匹配 re1, 但前面必须不是 re2 (结果子项不包含 re2)
re1 和 re2 表示正则表达式中的子表达式。
括号内的断言子表达式只做断言 (不消费原字符串), 前面/后面的子表达式可以继续匹配。
前瞻: 站在后面才能向前看, 断言的是后面, 即 前断后。
后视: 站在前面才能向后看, 断言的是前面, 即 后断前。
PS: Go 内置的 Regexp 目前好像还不支持前瞻后视。
代码示例:
package main
import (
"fmt"
"regexp"
)
func main() {
// 3 ~ 6个数字 (贪婪匹配, 最大匹配)
re := regexp.MustCompile(`\d{3,6}`)
fmt.Println(re.FindAllString("ab12345678cd", -1)) // [123456]
// 3 ~ 6个数字 (非贪婪匹配, 最小匹配)
re = regexp.MustCompile(`\d{3,6}?`)
fmt.Println(re.FindAllString("ab12345678cd", -1)) // [123 456]
// 3 个以上 纯数字 或 纯字母 (逻辑或)
re = regexp.MustCompile(`\d{3,}|[a-zA-Z]{3,}`)
fmt.Println(re.FindAllString("abc12de345", -1)) // [abc 345]
// 连续三个除了0-5以外的任意字符 (否定)
re = regexp.MustCompile(`[^0-5]{3}`)
fmt.Println(re.FindAllString("123456789abc", -1)) // [678 9ab]
// 忽略大小写 (全局/整个表达式捕获组内)
re = regexp.MustCompile(`(?i)abC`)
fmt.Println(re.FindAllString("12abC34ABc56", -1)) // [abC ABc]
// 忽略大小写 (仅捕获组内)
re = regexp.MustCompile(`a(?i:b)C`)
fmt.Println(re.FindAllString("12abC34ABc56", -1)) // [abC]
// 捕获组
re = regexp.MustCompile(`a([0-9]*)b`)
fmt.Println(re.FindAllString("12ab345a67b890", -1)) // [ab a67b]
fmt.Println(re.FindAllStringSubmatch("12ab345a67b890", -1)) // [[ab ] [a67b 67]] // 包含了捕获组
// 非捕获组
re = regexp.MustCompile(`a(?:[0-9]*)b`)
fmt.Println(re.FindAllStringSubmatch("12ab345a67b890", -1)) // [[ab] [a67b]]
}