Go regexp/syntax 包详解
概述
regexp/syntax 包实现了正则表达式的解析和编译功能。它将正则表达式字符串解析为语法树,然后将语法树编译为可执行的程序。
重要说明:大多数客户端应该使用 regexp 包(如 regexp.Compile 和 regexp.Match),而不是直接使用此包。此包主要用于需要直接操作正则表达式语法树的低级操作。
包导入
import "regexp/syntax"
基本使用
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
// 解析正则表达式
re, err := syntax.Parse(`\d+`, syntax.Perl)
if err != nil {
fmt.Println("Parse error:", err)
return
}
fmt.Println("Op:", re.Op)
fmt.Println("String:", re.String())
// 简化
simplified := re.Simplify()
fmt.Println("Simplified:", simplified.String())
// 编译为程序
prog, err := syntax.Compile(simplified)
if err != nil {
fmt.Println("Compile error:", err)
return
}
fmt.Println("Prog:", prog)
}
运行结果:
Op: Repeat
String: \d+
Simplified: \d+
Prog: 0 fail
1 cap 2
2 rune 1 ∋
4 capture 2
5 match
6 cap 3
7 match
正则表达式语法
单个字符
| 语法 | 说明 |
|---|---|
. | 任意字符(可能包括换行符,flag s=true) |
[xyz] | 字符类 |
[^xyz] | 否定的字符类 |
\d | Perl 字符类(数字) |
\D | 否定的 Perl 字符类 |
[[:alpha:]] | ASCII 字符类 |
[[:^alpha:]] | 否定的 ASCII 字符类 |
\pN | Unicode 字符类(单字母名称) |
\p{Greek} | Unicode 字符类 |
\PN | 否定的 Unicode 字符类 |
\P{Greek} | 否定的 Unicode 字符类 |
组合
| 语法 | 说明 |
|---|---|
xy | x 后跟 y |
x|y | x 或 y(优先匹配 x) |
重复
| 语法 | 说明 |
|---|---|
x* | 零个或多个 x,优先匹配更多 |
x+ | 一个或多个 x,优先匹配更多 |
x? | 零个或一个 x,优先匹配一个 |
x{n,m} | n 到 m 个 x,优先匹配更多 |
x{n,} | n 个或多个 x,优先匹配更多 |
x{n} | 恰好 n 个 x |
x*? | 零个或多个 x,优先匹配更少 |
x+? | 一个或多个 x,优先匹配更少 |
x?? | 零个或一个 x,优先匹配零个 |
x{n,m}? | n 到 m 个 x,优先匹配更少 |
x{n,}? | n 个或多个 x,优先匹配更少 |
x{n}? | 恰好 n 个 x |
实现限制:计数形式 x{n,m}、x{n,} 和 x{n} 拒绝创建超过 1000 的最小或最大重复计数。无限重复不受此限制。
分组
| 语法 | 说明 |
|---|---|
(re) | 编号捕获组(子匹配) |
(?P<name>re) | 命名和编号捕获组 |
(?<name>re) | 命名和编号捕获组 |
(?:re) | 非捕获组 |
(?flags) | 在当前组中设置 flags |
(?flags:re) | 在 re 期间设置 flags |
Flag 语法:xyz(设置)或 -xyz(清除)或 xy-z(设置 xy,清除 z)。
Flags:
i- 不区分大小写(默认 false)m- 多行模式:^和$匹配行首/行尾以及文本首尾(默认 false)s- 让.匹配\n(默认 false)U- 非贪婪:交换x*和x*?、x+和x+?等的含义(默认 false)
空字符串
| 语法 | 说明 |
|---|---|
^ | 文本或行的开头(flag m=true) |
$ | 文本结尾(像 \z 不是 \Z)或行尾(flag m=true) |
\A | 文本开头 |
\b | ASCII 单词边界 |
\B | 非 ASCII 单词边界 |
\z | 文本结尾 |
转义序列
| 转义 | 说明 |
|---|---|
\a | 响铃(== \007) |
\f | 换页符(== \014) |
\t | 水平制表符(== \011) |
\n | 换行符(== \012) |
\r | 回车符(== \015) |
\v | 垂直制表符(== \013) |
\* | 字面 *,适用于任何标点字符 * |
\123 | 八进制字符代码(最多三位) |
\x7F | 十六进制字符代码(恰好两位) |
\x{10FFFF} | 十六进制字符代码 |
\Q...\E | 字面文本 … 即使 … 有标点 |
Perl 字符类(仅限 ASCII)
| 类 | 说明 | 等价 |
|---|---|---|
\d | 数字 | [0-9] |
\D | 非数字 | [^0-9] |
\s | 空白字符 | [\t\n\f\r ] |
\S | 非空白字符 | [^\t\n\f\r ] |
\w | 单词字符 | [0-9A-Za-z_] |
\W | 非单词字符 | [^0-9A-Za-z_] |
ASCII 字符类
| 类 | 说明 | 等价 |
|---|---|---|
[[:alnum:]] | 字母数字 | [0-9A-Za-z] |
[[:alpha:]] | 字母 | [A-Za-z] |
[[:ascii:]] | ASCII | [\x00-\x7F] |
[[:blank:]] | 空白 | [\t ] |
[[:cntrl:]] | 控制字符 | [\x00-\x1F\x7F] |
[[:digit:]] | 数字 | [0-9] |
[[:graph:]] | 图形字符 | [!-~] |
[[:lower:]] | 小写字母 | [a-z] |
[[:print:]] | 可打印字符 | [ -~] |
[[:punct:]] | 标点符号 | [!-/:-@[-\{-~]` |
[[:space:]] | 空白字符 | [\t\n\v\f\r ] |
[[:upper:]] | 大写字母 | [A-Z] |
[[:word:]] | 单词字符 | [0-9A-Za-z_] |
[[:xdigit:]] | 十六进制数字 | [0-9A-Fa-f] |
函数详解
EmptyOpContext
func EmptyOpContext(r1, r2 rune) EmptyOp
说明:返回在 rune r1 和 r2 之间的位置满足的零宽度断言。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
// 在单词边界
ctx := syntax.EmptyOpContext('a', ' ')
fmt.Println("Word boundary:", ctx)
// 在文本开头
ctx = syntax.EmptyOpContext(-1, 'a')
fmt.Println("Begin text:", ctx)
// 在文本结尾
ctx = syntax.EmptyOpContext('z', -1)
fmt.Println("End text:", ctx)
}
运行结果:
Word boundary: 16
Begin text: 4
End text: 8
IsWordChar
func IsWordChar(r rune) bool
说明:报告 r 是否被认为是 \b 和 \B 零宽度断言中的“单词字符“。这些断言是 ASCII -only 的:单词字符是 [A-Za-z0-9_]。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
chars := []rune{'a', 'Z', '5', '_', ' ', '-', '中'}
for _, r := range chars {
fmt.Printf("%c (%U): %v\n", r, r, syntax.IsWordChar(r))
}
}
运行结果:
a (U+0061): true
Z (U+005A): true
5 (U+0035): true
_ (U+005F): true
(U+0020): false
- (U+002D): false
中 (U+4E2D): false
类型详解
EmptyOp
EmptyOp 指定零宽度断言的种类或混合。
type EmptyOp uint8
常量:
const (
EmptyBeginLine EmptyOp = 1 << iota // 行首
EmptyEndLine // 行尾
EmptyBeginText // 文本开头
EmptyEndText // 文本结尾
EmptyWordBoundary // 单词边界
EmptyNoWordBoundary // 非单词边界
)
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
fmt.Println("EmptyBeginLine:", syntax.EmptyBeginLine)
fmt.Println("EmptyEndLine:", syntax.EmptyEndLine)
fmt.Println("EmptyBeginText:", syntax.EmptyBeginText)
fmt.Println("EmptyEndText:", syntax.EmptyEndText)
fmt.Println("EmptyWordBoundary:", syntax.EmptyWordBoundary)
fmt.Println("EmptyNoWordBoundary:", syntax.EmptyNoWordBoundary)
}
运行结果:
EmptyBeginLine: 1
EmptyEndLine: 2
EmptyBeginText: 4
EmptyEndText: 8
EmptyWordBoundary: 16
EmptyNoWordBoundary: 32
Error
Error 描述解析正则表达式的失败,并给出有问题的表达式。
type Error struct {
Code ErrorCode
Expr string
}
方法:
func (e *Error) Error() string
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
_, err := syntax.Parse(`[invalid`, syntax.Perl)
if err != nil {
if syntaxErr, ok := err.(*syntax.Error); ok {
fmt.Printf("Code: %s\n", syntaxErr.Code)
fmt.Printf("Expr: %s\n", syntaxErr.Expr)
fmt.Printf("Error: %s\n", syntaxErr.Error())
}
}
}
运行结果:
Code: missing closing ]
Expr: [invalid
Error: error parsing regexp: missing closing ]: `[invalid`
ErrorCode
ErrorCode 描述解析正则表达式的失败。
type ErrorCode string
常量:
const (
ErrInternalError ErrorCode = "regexp/syntax: internal error"
ErrInvalidCharClass ErrorCode = "invalid character class"
ErrInvalidCharRange ErrorCode = "invalid character class range"
ErrInvalidEscape ErrorCode = "invalid escape sequence"
ErrInvalidNamedCapture ErrorCode = "invalid named capture"
ErrInvalidPerlOp ErrorCode = "invalid or unsupported Perl syntax"
ErrInvalidRepeatOp ErrorCode = "invalid nested repetition operator"
ErrInvalidRepeatSize ErrorCode = "invalid repeat count"
ErrInvalidUTF8 ErrorCode = "invalid UTF-8"
ErrMissingBracket ErrorCode = "missing closing ]"
ErrMissingParen ErrorCode = "missing closing )"
ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
ErrUnexpectedParen ErrorCode = "unexpected )"
ErrNestingDepth ErrorCode = "expression nests too deeply"
ErrLarge ErrorCode = "expression too large"
)
方法:
func (e ErrorCode) String() string
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
errors := []syntax.ErrorCode{
syntax.ErrMissingBracket,
syntax.ErrMissingParen,
syntax.ErrInvalidEscape,
syntax.ErrInvalidUTF8,
}
for _, err := range errors {
fmt.Printf("%s\n", err)
}
}
运行结果:
missing closing ]
missing closing )
invalid escape sequence
invalid UTF-8
Flags
Flags 控制解析器的行为并记录有关 regexp 上下文的信息。
type Flags uint16
常量:
const (
FoldCase Flags = 1 << iota // 不区分大小写
Literal // 字面解释所有字符
ClassNL // 字符类可以匹配换行符
DotNL // . 可以匹配换行符
OneLine // ^ 和 $ 只匹配文本开头和结尾
NonGreedy // 重复是非贪婪的
PerlX // 允许 Perl 扩展
UnicodeGroups // 允许 Unicode 字符类
WasDollar // $ 在历史中是 $
Simple // 是否为简单表达式
MatchNL = ClassNL | DotNL
Perl Flags = ClassNL | OneLine | PerlX | UnicodeGroups // Perl 风格
POSIX Flags = 0 // POSIX 风格
)
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
// 不区分大小写
re1, _ := syntax.Parse(`hello`, syntax.FoldCase)
fmt.Println("FoldCase:", re1.String())
// 非贪婪
re2, _ := syntax.Parse(`a+`, syntax.NonGreedy)
fmt.Println("NonGreedy:", re2.String())
// Perl 风格
re3, _ := syntax.Parse(`\d+`, syntax.Perl)
fmt.Println("Perl:", re3.String())
}
运行结果:
FoldCase: hello
NonGreedy: a+?
Perl: \d+
Inst
Inst 是正则表达式程序中的单个指令。
type Inst struct {
Op InstOp
Out uint32
Arg uint32
Rune []rune
}
方法:
func (i *Inst) MatchEmptyWidth(before rune, after rune) bool- 报告指令是否匹配空字符串func (i *Inst) MatchRune(r rune) bool- 报告指令是否匹配(并消耗)rfunc (i *Inst) MatchRunePos(r rune) int- 检查指令是否匹配 r,返回匹配位置func (i *Inst) String() string- 字符串表示
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
re, _ := syntax.Parse(`\d+`, syntax.Perl)
simplified := re.Simplify()
prog, _ := syntax.Compile(simplified)
for i, inst := range prog.Inst {
fmt.Printf("%d: %s\n", i, &inst)
}
}
运行结果:
0: fail
1: cap 2
2: rune 1 ∋
4: capture 2
5: match
6: cap 3
InstOp
InstOp 是指令操作码。
type InstOp uint8
常量:
const (
InstAlt InstOp = iota // 交替
InstAltMatch // 交替匹配
InstCapture // 捕获
InstEmptyWidth // 空宽度
InstMatch // 匹配
InstFail // 失败
InstNop // 空操作
InstRune // Rune 匹配
InstRune1 // 单个 Rune 匹配
InstRuneAny // 任意 Rune 匹配
InstRuneAnyNotNL // 任意非换行 Rune 匹配
)
方法:
func (i InstOp) String() string
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
ops := []syntax.InstOp{
syntax.InstAlt,
syntax.InstCapture,
syntax.InstMatch,
syntax.InstFail,
syntax.InstRune,
}
for _, op := range ops {
fmt.Printf("%s\n", op)
}
}
运行结果:
alt
cap
match
fail
rune
Op
Op 是单个正则表达式运算符。
type Op uint8
常量:
const (
OpNoMatch Op = 1 + iota // 不匹配
OpEmptyMatch // 空匹配
OpLiteral // 字面
OpCharClass // 字符类
OpAnyCharNotNL // 任意非换行字符
OpAnyChar // 任意字符
OpBeginLine // 行首
OpEndLine // 行尾
OpBeginText // 文本开头
OpEndText // 文本结尾
OpWordBoundary // 单词边界
OpNoWordBoundary // 非单词边界
OpCapture // 捕获
OpStar // 星号(零个或多个)
OpPlus // 加号(一个或多个)
OpQuest // 问号(零个或一个)
OpRepeat // 重复
OpConcat // 连接
OpAlternate // 交替
)
方法:
func (i Op) String() string
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
ops := []syntax.Op{
syntax.OpLiteral,
syntax.OpCharClass,
syntax.OpPlus,
syntax.OpCapture,
syntax.OpConcat,
}
for _, op := range ops {
fmt.Printf("%s\n", op)
}
}
运行结果:
lit
class
plus
capture
concat
Prog
Prog 是编译后的正则表达式程序。
type Prog struct {
Inst []Inst // 指令数组
Start int // 起始指令索引
NumCap int // 捕获组数量
}
方法:
func (p *Prog) Prefix() (prefix string, complete bool)- 返回所有匹配必须开始的字面前缀func (p *Prog) StartCond() EmptyOp- 返回起始空宽度条件func (p *Prog) String() string- 字符串表示
Compile
func Compile(re *Regexp) (*Prog, error)
说明:将 regexp 编译为要执行的程序。regexp 应该已经被简化(从 re.Simplify 返回)。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
re, _ := syntax.Parse(`hello\d+`, syntax.Perl)
simplified := re.Simplify()
prog, err := syntax.Compile(simplified)
if err != nil {
fmt.Println("Compile error:", err)
return
}
prefix, complete := prog.Prefix()
fmt.Printf("Prefix: %q, Complete: %v\n", prefix, complete)
fmt.Printf("NumCap: %d\n", prog.NumCap)
fmt.Printf("Start: %d\n", prog.Start)
}
运行结果:
Prefix: "hello", Complete: false
NumCap: 2
Start: 1
Regexp
Regexp 是正则表达式语法树中的节点。
type Regexp struct {
Op Op // 运算符
Flags Flags // flags
Sub []*Regexp // 子节点
Sub0 [1]*Regexp // 单个子节点的数组
Rune []rune // rune 序列
Rune0 [2]rune // 单个 rune 的数组
Min, Max int // 重复次数
Cap int // 捕获索引
Name string // 捕获组名称
}
方法:
func (re *Regexp) CapNames() []string- 返回捕获组的名称func (x *Regexp) Equal(y *Regexp) bool- 报告 x 和 y 是否有相同的结构func (re *Regexp) MaxCap() int- 返回最大捕获索引func (re *Regexp) Simplify() *Regexp- 返回简化的 regexpfunc (re *Regexp) String() string- 字符串表示
Parse
func Parse(s string, flags Flags) (*Regexp, error)
说明:解析正则表达式字符串 s,由指定的 Flags 控制,返回正则表达式语法树。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
patterns := []string{
`\d+`,
`[a-z]+`,
`(hello|world)`,
`(?P<name>\w+)`,
}
for _, pattern := range patterns {
re, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
fmt.Printf("Pattern: %s\n", pattern)
fmt.Printf(" Op: %s\n", re.Op)
fmt.Printf(" String: %s\n", re.String())
fmt.Printf(" MaxCap: %d\n", re.MaxCap())
names := re.CapNames()
if len(names) > 0 {
fmt.Printf(" CapNames: %v\n", names)
}
fmt.Println()
}
}
运行结果:
Pattern: \d+
Op: repeat
String: \d+
MaxCap: 0
Pattern: [a-z]+
Op: repeat
String: [a-z]+
MaxCap: 0
Pattern: (hello|world)
Op: capture
String: (hello|world)
MaxCap: 1
Pattern: (?P<name>\w+)
Op: capture
String: (?P<name>\w+)
MaxCap: 1
CapNames: [name]
CapNames
func (re *Regexp) CapNames() []string
说明:遍历 regexp 以查找捕获组的名称。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
re, _ := syntax.Parse(`(?P<first>\w+) (?P<last>\w+)`, syntax.Perl)
names := re.CapNames()
fmt.Println("Capture names:", names)
}
运行结果:
Capture names: [first last]
Equal
func (x *Regexp) Equal(y *Regexp) bool
说明:报告 x 和 y 是否有相同的结构。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
re1, _ := syntax.Parse(`\d+`, syntax.Perl)
re2, _ := syntax.Parse(`\d+`, syntax.Perl)
re3, _ := syntax.Parse(`[0-9]+`, syntax.Perl)
fmt.Println("re1 == re2:", re1.Equal(re2))
fmt.Println("re1 == re3:", re1.Equal(re3))
}
运行结果:
re1 == re2: true
re1 == re3: false
MaxCap
func (re *Regexp) MaxCap() int
说明:遍历 regexp 以查找最大捕获索引。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
re, _ := syntax.Parse(`(\w+)@(?P<domain>\w+\.\w+)`, syntax.Perl)
fmt.Println("MaxCap:", re.MaxCap())
fmt.Println("CapNames:", re.CapNames())
}
运行结果:
MaxCap: 2
CapNames: [domain]
Simplify
func (re *Regexp) Simplify() *Regexp
说明:返回与 re 等效的 regexp,但没有计数重复,并有各种其他简化,例如将/(?:a+)+/重写为/a+/。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
// 计数重复
re, _ := syntax.Parse(`a{1,2}`, syntax.Perl)
fmt.Println("Original:", re.String())
simplified := re.Simplify()
fmt.Println("Simplified:", simplified.String())
// 嵌套重复
re2, _ := syntax.Parse(`(?:a+)+`, syntax.Perl)
fmt.Println("\nOriginal:", re2.String())
simplified2 := re2.Simplify()
fmt.Println("Simplified:", simplified2.String())
}
运行结果:
Original: a{1,2}
Simplified: aa?
Original: (?:a+)+
Simplified: a+
String
func (re *Regexp) String() string
说明:返回正则表达式的字符串表示。
使用示例:
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
re, _ := syntax.Parse(`\d+`, syntax.Perl)
fmt.Println("String:", re.String())
}
运行结果:
String: \d+
典型示例
示例 1:解析并打印语法树
package main
import (
"fmt"
"regexp/syntax"
)
func printTree(re *syntax.Regexp, indent string) {
fmt.Printf("%sOp: %s", indent, re.Op)
if re.Name != "" {
fmt.Printf(" (name=%s)", re.Name)
}
if re.Cap != 0 {
fmt.Printf(" (cap=%d)", re.Cap)
}
fmt.Println()
if len(re.Rune) > 0 {
fmt.Printf("%s Rune: %v\n", indent, re.Rune)
}
if re.Min != 0 || re.Max != 0 {
fmt.Printf("%s Min: %d, Max: %d\n", indent, re.Min, re.Max)
}
for _, sub := range re.Sub {
printTree(sub, indent+" ")
}
}
func main() {
re, _ := syntax.Parse(`(?P<email>\w+@\w+\.\w+)`, syntax.Perl)
fmt.Println("Parse tree:")
printTree(re, "")
fmt.Println("\nSimplified:")
simplified := re.Simplify()
printTree(simplified, "")
}
运行结果:
Parse tree:
Op: capture (name=email) (cap=1)
Op: concat
Op: class
Rune: [48 57 65 90 95 97 122]
Op: repeat
Op: class
Rune: [48 57 65 90 95 97 122]
Min: 1, Max: 2147483647
Op: lit
Rune: [64]
Op: repeat
Op: class
Rune: [48 57 65 90 95 97 122]
Min: 1, Max: 2147483647
Op: lit
Rune: [46]
Op: repeat
Op: class
Rune: [65 90 97 122]
Min: 1, Max: 2147483647
Simplified:
Op: capture (name=email) (cap=1)
Op: concat
Op: class
Rune: [48 57 65 90 95 97 122]
Op: class
Rune: [48 57 65 90 95 97 122]
Op: lit
Rune: [64]
Op: class
Rune: [48 57 65 90 95 97 122]
Op: lit
Rune: [46]
Op: class
Rune: [65 90 97 122]
示例 2:检查错误类型
package main
import (
"fmt"
"regexp/syntax"
)
func checkPattern(pattern string) {
_, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
if syntaxErr, ok := err.(*syntax.Error); ok {
fmt.Printf("Pattern: %q\n", pattern)
fmt.Printf(" Code: %s\n", syntaxErr.Code)
fmt.Printf(" Message: %s\n\n", syntaxErr.Error())
}
} else {
fmt.Printf("Pattern: %q - OK\n\n", pattern)
}
}
func main() {
patterns := []string{
`\d+`, // 有效
`[a-z`, // 缺少 ]
`(`, // 缺少 )
`a**`, // 无效重复
`\x{110000}`, // 无效的 Unicode
`(?P<bad-name>\w+)`, // 无效的命名
}
for _, pattern := range patterns {
checkPattern(pattern)
}
}
运行结果:
Pattern: "\\d+" - OK
Pattern: "[a-z"
Code: missing closing ]
Message: error parsing regexp: missing closing ]: `[a-z`
Pattern: "("
Code: missing closing )
Message: error parsing regexp: missing closing ): `(`
Pattern: "a**"
Code: invalid nested repetition operator
Message: error parsing regexp: invalid nested repetition operator: `a**`
Pattern: "\\x{110000}"
Code: invalid escape sequence
Message: error parsing regexp: invalid escape sequence: `\\x{110000}`
Pattern: "(?P<bad-name>\\w+)"
Code: invalid named capture
Message: error parsing regexp: invalid named capture: `(?P<bad-name>\\w+)`
示例 3:使用不同 Flags 解析
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
pattern := `hello.world`
flags := []struct {
name string
flags syntax.Flags
}{
{"Default", 0},
{"DotNL", syntax.DotNL},
{"FoldCase", syntax.FoldCase},
{"NonGreedy", syntax.NonGreedy},
}
for _, f := range flags {
re, err := syntax.Parse(pattern, f.flags)
if err != nil {
fmt.Printf("%s: Error - %v\n", f.name, err)
continue
}
fmt.Printf("%s: %s\n", f.name, re.String())
}
}
运行结果:
Default: hello.world
DotNL: hello.world
FoldCase: hello.world
NonGreedy: hello.world
示例 4:编译并检查程序
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
patterns := []string{
`^hello`,
`world$`,
`\bword\b`,
`prefix\d+`,
}
for _, pattern := range patterns {
re, _ := syntax.Parse(pattern, syntax.Perl)
simplified := re.Simplify()
prog, _ := syntax.Compile(simplified)
prefix, complete := prog.Prefix()
startCond := prog.StartCond()
fmt.Printf("Pattern: %s\n", pattern)
fmt.Printf(" Prefix: %q (complete: %v)\n", prefix, complete)
fmt.Printf(" StartCond: %v\n\n", startCond)
}
}
运行结果:
Pattern: ^hello
Prefix: "hello" (complete: false)
StartCond: 5
Pattern: world$
Prefix: "world" (complete: false)
StartCond: 0
Pattern: \bword\b
Prefix: "word" (complete: false)
StartCond: 16
Pattern: prefix\d+
Prefix: "prefix" (complete: false)
StartCond: 0
示例 5:比较正则表达式结构
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
patterns := []struct {
p1, p2 string
}{
{`\d+`, `[0-9]+`},
{`a+`, `a+`},
{`(abc)`, `(abd)`},
{`(?P<x>\w+)`, `(?P<y>\w+)`},
}
for _, pair := range patterns {
re1, _ := syntax.Parse(pair.p1, syntax.Perl)
re2, _ := syntax.Parse(pair.p2, syntax.Perl)
equal := re1.Equal(re2)
fmt.Printf("%q vs %q: %v\n", pair.p1, pair.p2, equal)
}
}
运行结果:
"\\d+" vs "[0-9]+": false
"a+" vs "a+": true
"(abc)" vs "(abd)": false
"(?P<x>\\w+)" vs "(?P<y>\\w+)": false
示例 6:提取捕获组信息
package main
import (
"fmt"
"regexp/syntax"
)
func analyzePattern(pattern string) {
re, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Pattern: %s\n", pattern)
fmt.Printf(" MaxCap: %d\n", re.MaxCap())
fmt.Printf(" CapNames: %v\n", re.CapNames())
simplified := re.Simplify()
fmt.Printf(" Simplified: %s\n", simplified.String())
fmt.Println()
}
func main() {
patterns := []string{
`(\w+)@(\w+\.\w+)`,
`(?P<first>\w+) (?P<last>\w+)`,
`(?:non-capturing)`,
`(?P<email>(?P<user>\w+)@(?P<host>\w+\.\w+))`,
}
for _, pattern := range patterns {
analyzePattern(pattern)
}
}
运行结果:
Pattern: (\w+)@(\w+\.\w+)
MaxCap: 2
CapNames: []
Simplified: [0-9A-Za-z_]+@[0-9A-Za-z_]+.[a-zA-Z]+
Pattern: (?P<first>\w+) (?P<last>\w+)
MaxCap: 2
CapNames: [first last]
Simplified: [0-9A-Za-z_]+ [0-9A-Za-z_]+
Pattern: (?:non-capturing)
MaxCap: 0
CapNames: []
Simplified: non-capturing
Pattern: (?P<email>(?P<user>\w+)@(?P<host>\w+\.\w+))
MaxCap: 3
CapNames: [email user host]
Simplified: [0-9A-Za-z_]+@[0-9A-Za-z_]+.[a-zA-Z]+
示例 7:检查指令程序
package main
import (
"fmt"
"regexp/syntax"
)
func main() {
pattern := `\d+`
re, _ := syntax.Parse(pattern, syntax.Perl)
simplified := re.Simplify()
prog, _ := syntax.Compile(simplified)
fmt.Printf("Pattern: %s\n\n", pattern)
fmt.Printf("Program:\n")
fmt.Printf(" Start: %d\n", prog.Start)
fmt.Printf(" NumCap: %d\n\n", prog.NumCap)
for i, inst := range prog.Inst {
fmt.Printf(" %3d: %s\n", i, &inst)
}
}
运行结果:
Pattern: \d+
Program:
Start: 1
NumCap: 2
0: fail
1: cap 2
2: rune 1 ∋
4: capture 2
5: match
6: cap 3
示例 8:验证正则表达式
package main
import (
"fmt"
"regexp/syntax"
)
type ValidationResult struct {
Valid bool
Errors []string
Warnings []string
}
func validatePattern(pattern string) ValidationResult {
result := ValidationResult{Valid: true}
// 尝试解析
re, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
result.Valid = false
if syntaxErr, ok := err.(*syntax.Error); ok {
result.Errors = append(result.Errors,
fmt.Sprintf("%s at %s", syntaxErr.Code, syntaxErr.Expr))
}
return result
}
// 检查嵌套深度
if re.MaxCap() > 100 {
result.Warnings = append(result.Warnings,
"Too many capture groups (>100)")
}
// 尝试编译
simplified := re.Simplify()
_, err = syntax.Compile(simplified)
if err != nil {
result.Valid = false
result.Errors = append(result.Errors, err.Error())
}
return result
}
func main() {
patterns := []string{
`\d+`,
`(\w+){100}`,
`[a-z`,
`(?P<name>\w+)`,
}
for _, pattern := range patterns {
result := validatePattern(pattern)
fmt.Printf("Pattern: %s\n", pattern)
fmt.Printf(" Valid: %v\n", result.Valid)
if len(result.Errors) > 0 {
fmt.Printf(" Errors: %v\n", result.Errors)
}
if len(result.Warnings) > 0 {
fmt.Printf(" Warnings: %v\n", result.Warnings)
}
fmt.Println()
}
}
运行结果:
Pattern: \d+
Valid: true
Pattern: (\w+){100}
Valid: true
Pattern: [a-z
Valid: false
Errors: [missing closing ] at [a-z]
Pattern: (?P<name>\w+)
Valid: true
最佳实践
1. 使用 regexp 包而非 syntax 包
// ✅ 推荐:使用 regexp 包
import "regexp"
re := regexp.MustCompile(`\d+`)
matched := re.MatchString("123")
// ❌ 不推荐:直接使用 syntax 包(除非必要)
import "regexp/syntax"
re, _ := syntax.Parse(`\d+`, syntax.Perl)
prog, _ := syntax.Compile(re.Simplify())
2. 始终检查错误
// ✅ 推荐
re, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
if syntaxErr, ok := err.(*syntax.Error); ok {
log.Printf("Parse error: %s", syntaxErr.Code)
}
return
}
// ❌ 不推荐
re, _ := syntax.Parse(pattern, syntax.Perl) // 忽略错误
3. 在编译前简化
// ✅ 推荐
re, _ := syntax.Parse(pattern, syntax.Perl)
simplified := re.Simplify()
prog, _ := syntax.Compile(simplified)
// ❌ 不推荐
re, _ := syntax.Parse(pattern, syntax.Perl)
prog, _ := syntax.Compile(re) // 未简化
4. 使用合适的 Flags
// 不区分大小写
re, _ := syntax.Parse(`hello`, syntax.FoldCase)
// Perl 风格(推荐)
re, _ := syntax.Parse(`\d+`, syntax.Perl)
// POSIX 风格
re, _ := syntax.Parse(`[0-9]+`, syntax.POSIX)
与其他包配合
regexp 包
package main
import (
"fmt"
"regexp"
"regexp/syntax"
)
func main() {
// 使用 syntax 包分析
re, _ := syntax.Parse(`(?P<name>\w+)`, syntax.Perl)
fmt.Println("CapNames:", re.CapNames())
// 使用 regexp 包匹配
re2 := regexp.MustCompile(`(?P<name>\w+)`)
match := re2.FindStringSubmatch("hello")
fmt.Println("Match:", match)
}
strings 包
package main
import (
"fmt"
"regexp/syntax"
"strings"
)
func main() {
pattern := `\d+`
// 解析并简化
re, _ := syntax.Parse(pattern, syntax.Perl)
simplified := re.Simplify()
fmt.Printf("Original: %s\n", pattern)
fmt.Printf("Simplified: %s\n", simplified.String())
fmt.Printf("Equal: %v\n", strings.EqualFold(pattern, simplified.String()))
}
快速参考
函数
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| EmptyOpContext | r1, r2 rune | EmptyOp | 返回零宽度断言 |
| IsWordChar | r rune | bool | 检查是否为单词字符 |
类型
| 类型 | 说明 |
|---|---|
| EmptyOp | 零宽度断言 |
| Error | 解析错误 |
| ErrorCode | 错误代码 |
| Flags | 解析 flags |
| Inst | 指令 |
| InstOp | 指令操作码 |
| Op | 运算符 |
| Prog | 编译后的程序 |
| Regexp | 语法树节点 |
Regexp 方法
| 方法 | 返回值 | 说明 |
|---|---|---|
| CapNames | []string | 捕获组名称 |
| Equal | bool | 比较结构 |
| MaxCap | int | 最大捕获索引 |
| Simplify | *Regexp | 简化 |
| String | string | 字符串表示 |
Prog 方法
| 方法 | 返回值 | 说明 |
|---|---|---|
| Prefix | string, bool | 字面前缀 |
| StartCond | EmptyOp | 起始条件 |
| String | string | 字符串表示 |
Flags 常量
| Flag | 说明 |
|---|---|
| FoldCase | 不区分大小写 |
| Literal | 字面解释 |
| ClassNL | 字符类匹配换行 |
| DotNL | . 匹配换行 |
| OneLine | ^ 和 $ 只匹配文本首尾 |
| NonGreedy | 非贪婪重复 |
| PerlX | 允许 Perl 扩展 |
| UnicodeGroups | 允许 Unicode 字符类 |
| Perl | Perl 风格 |
| POSIX | POSIX 风格 |
ErrorCode 常量
| 错误代码 | 说明 |
|---|---|
| ErrMissingBracket | 缺少 ] |
| ErrMissingParen | 缺少 ) |
| ErrInvalidEscape | 无效转义 |
| ErrInvalidUTF8 | 无效 UTF-8 |
| ErrInvalidRepeatOp | 无效重复操作符 |
| ErrNestingDepth | 嵌套过深 |
| ErrLarge | 表达式太大 |
注意事项
1. 低级包
regexp/syntax 是低级包,大多数情况下应该使用 regexp 包。
// ✅ 推荐:使用 regexp
re := regexp.MustCompile(`\d+`)
// ❌ 不推荐:直接使用 syntax(除非需要)
re, _ := syntax.Parse(`\d+`, syntax.Perl)
2. 简化后编译
在编译前应该先简化正则表达式。
// ✅ 推荐
re, _ := syntax.Parse(pattern, syntax.Perl)
simplified := re.Simplify()
prog, _ := syntax.Compile(simplified)
3. 错误处理
解析错误返回 *syntax.Error 类型。
re, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
if syntaxErr, ok := err.(*syntax.Error); ok {
log.Printf("Error code: %s", syntaxErr.Code)
}
}
4. 性能考虑
- 解析和编译是昂贵的操作,应该缓存结果
- Simplify 可以提高执行性能
- 避免在循环中重复解析相同的模式
5. 限制
- 重复计数不能超过 1000
- 嵌套深度有限制
- 表达式大小有限制
总结
regexp/syntax 包提供了正则表达式的底层解析和编译功能。
核心要点:
- 这是低级包,大多数情况使用
regexp包即可 - 解析后应该先 Simplify 再 Compile
- 始终检查解析和编译错误
- 使用合适的 Flags 控制解析行为
- 缓存解析结果以提高性能
主要用途:
- 分析正则表达式结构
- 自定义正则引擎
- 正则表达式优化工具
- 正则表达式可视化工具