正则高级语法

当前位置:首页>正则>正则高级语法

正则高级语法

# PCRE2高级语法参考手册


## 1. 反向引用(Backreferences)


反向引用允许在正则表达式中引用前面捕获组所匹配的内容。


### 1.1 数字反向引用

- `\1`, `\g1`, `\g{1}` - 引用第1个捕获组

- `\2`, `\g2`, `\g{2}` - 引用第2个捕获组

- `\3`, `\g3`, `\g{3}` - 引用第3个捕获组


**示例:**

- 文本: "abcabc"

- 表达式: `(abc)\1`

- 预期结果: 匹配整个字符串"abcabc"


- 文本: "hello world hello"

- 表达式: `(\w+)\s+\w+\s+(\1)`

- 预期结果: 匹配整个字符串,第二个捕获组捕获"hello"


### 1.2 相对反向引用

- `\g{-1}`, `\g{-2}`... - 相对于当前位置向前数的捕获组

- `\g{+1}`, `\g{+2}`... - 相对于当前位置向后数的捕获组


**示例:**

- 文本: "xyxy"

- 表达式: `(x)(y)\g{-2}\g{-1}`

- 预期结果: 匹配整个字符串"xyxy"


### 1.3 命名反向引用

- `\k<name>` - Perl风格的命名反向引用

- `\k'name'` - 单引号语法

- `\g{name}` - 通用命名引用语法

- `(?P=name)` - Python风格语法


**示例:**

- 文本: "hello world hello"

- 表达式: `(?<word>\w+)\s+\w+\s+(\k<word>)`

- 预期结果: 匹配整个字符串,第二个捕获组捕获"hello"


## 2. 递归表达式(Recursive Expressions)


递归表达式允许正则表达式匹配嵌套或递归的结构。


### 2.1 整体模式递归

- `(?R)` - 递归整个正则表达式模式

- `(?0)` - 与`(?R)`相同,递归整个模式


**示例:**

- 文本: "(a(b(c)d)e)"

- 表达式: `\(([^()]++|(?R))*\)`

- 预期结果: 匹配整个字符串"(a(b(c)d)e)"


### 2.2 捕获组递归

- `(?1)`, `(?2)`, `(?n)` - 递归引用第1、2、n个捕获组

- `(?+n)` - 相对递归,向后数第n个捕获组

- `(?-n)` - 相对递归,向前数第n个捕获组


**示例:**

- 文本: "(a(b)c)"

- 表达式: `(\(([^()]++|(?1))*\))`

- 预期结果: 匹配整个字符串"(a(b)c)"


- 文本: "((()))"

- 表达式: `(\(([^()]++|(?1))*\))`

- 预期结果: 匹配整个字符串"((()))"


### 2.3 命名递归

- `(?&name)` - 递归名为name的捕获组(Perl语法)

- `(?P>name)` - Python风格语法

- `\g<name>` - Oniguruma风格语法

- `\g'name'` - Oniguruma单引号语法


**示例:**

- 文本: "(a(b)c)"

- 表达式: `(?<paren>\(([^()]++|(?&paren))*\))`

- 预期结果: 匹配整个字符串"(a(b)c)"


### 2.4 带返回值的递归(PCRE2扩展,TODO:尚未封装支持)

- `(?R(grouplist))` - 递归整个模式并返回指定捕获组

- `(?n(grouplist))` - 递归第n个组并返回指定捕获组

- `(?&name(grouplist))` - 递归命名组并返回指定捕获组


**示例:**

- 文本: "Saturday,Sat"

- 表达式: `(?x: # 忽略空格以便清晰

    # 定义一个可重用的例程"weekendday",它匹配Saturday或

    # Sunday,并返回Sat/Sun前缀作为\k<short>。

    (?(DEFINE) (?<weekendday>

        (?|(?<short>Sat)urday|(?<short>Sun)day) ) )

    # 调用例程。匹配"Saturday,Sat"或"Sunday,Sun"。

    (?&weekendday(<short>)),\k<short> )`

- 预期结果: 匹配整个字符串"Saturday,Sat",并返回命名捕获组short="Sat"


### 2.5 递归条件检测

- `(?(R)yes-pattern|no-pattern)` - 检查是否在递归中

- `(?(R1)yes-pattern|no-pattern)` - 检查是否在第1组的递归中

- `(?(R&name)yes-pattern|no-pattern)` - 检查是否在名为name的组的递归中


**示例:**

- 文本: "<div><span>text</span></div>"

- 表达式: `<(\w+)(?>(?:"[^"]*"|'[^']*'|[^>])*?)>(?:(?R)|[^<]*)</\1>`

- 预期结果: 匹配整个HTML标签结构


## 3. 原子分组(Atomic Groups)


原子分组阻止回溯进入分组内部。


### 3.1 语法

- `(?>...)` - 原子分组

- `(*atomic:...)` - Perl 5.28 引入的字母形式


### 3.2 示例

- 文本: "aardvark"

- 表达式: `(?>a*)a`

- 预期结果: 不匹配(原子分组消耗所有a,然后外面的a无法匹配)


- 文本: "abc123def"

- 表达式: `(?>[a-z]*)\d+`

- 预期结果: 匹配"abc123"(虽然只应匹配"123",但原子分组会消耗整个"abc")


批注:贪婪懒惰占有模式

1、Greedy 贪婪但回溯

* + ? {n,m}

尝试匹配尽量多的字符,但如果后面的正则无法匹配成功,它会吐出字符,尝试让后面的正则匹配

2、Lazy 懒惰

*? +? ?? {n,m}?

匹配尽可能少的字符,匹配后,如果后面的正则无法匹配成功,它会向前吃字符(回溯进食),尝试让后面的模式匹配成功

3、Possessive 霸道占有

尝试匹配尽量多的字符,且坚决不回吐

*+ ++ ?+ {n,m}+


## 4. 占有量词(Possessive Quantifiers)


占有量词表示贪婪匹配且不允许回溯。


### 4.1 语法

- `*+`, `++`, `?+`, `{n,m}+` - 在普通限定符后加`+`


### 4.2 示例

- 文本: "aaaaa"

- 表达式: `a++b`

- 预期结果: 不匹配(a++消耗所有a,没有剩余的给b)


- 文本: "aaaaa"

- 表达式: `a*a`

- 预期结果: 匹配"aaaaa"(普通限定符允许回溯)


## 5. 条件表达式(Conditional Groups)


根据条件选择不同的匹配分支。


### 5.1 捕获组条件

- `(?(1)yes-pattern|no-pattern)` - 如果第1个捕获组已匹配则使用yes-pattern

- `(?(<name>)yes-pattern|no-pattern)` - 如果名为name的捕获组已匹配则使用yes-pattern


**示例:**

- 文本: "(abc)123"

- 表达式: `(\()?abc(?(1)\)|123)`

- 预期结果: 匹配整个字符串"(abc)123"


- 文本: "abc123"

- 表达式: `(\()?abc(?(1)\)|123)`

- 预期结果: 匹配整个字符串"abc123"


### 5.2 递归条件

- `(?(R)yes-pattern|no-pattern)` - 如果正在递归则使用yes-pattern

- `(?(R1)yes-pattern|no-pattern)` - 如果正在递归第1个组则使用yes-pattern


### 5.3 断言条件

- `(?(?=assertion)yes-pattern|no-pattern)` - 前瞻断言为真时使用yes-pattern

- `(?(?!assertion)yes-pattern|no-pattern)` - 负前瞻断言为真时使用yes-pattern


**示例:**

- 文本: "password123"

- 表达式: `(?(?=\d)123|abc)`

- 预期结果: 不匹配(因为字符串以字母开头,不是数字)

- 文本: "123abc"

- 表达式: `(?(?=\d)123|abc)`

- 预期结果: 匹配"123"


## 6. 断言(Assertions)


断言用于测试某个条件是否满足,但不消耗字符。


### 6.1 前瞻断言

- `(?=...)` - 正向前瞻断言

- `(?!...)` - 负向前瞻断言


**示例:**

- 文本: "password123"

- 表达式: `\w+(?=123)`

- 预期结果: 匹配"password"(但不包括"123")


- 文本: "password456"

- 表达式: `\w+(?!123)`

- 预期结果: 匹配"password456"


### 6.2 后顾断言

- `(?<=...)` - 正向后顾断言(固定长度)

- `(?<!...)` - 负向后顾断言(固定长度)


**示例:**

- 文本: "price:100"

- 表达式: `(?<=:)\d+`

- 预期结果: 匹配"100"


- 文本: "amount200"

- 表达式: `(?<!:)\d+`

- 预期结果: 匹配"200"


### 6.3 非原子断言(PCRE2扩展)

- `(*napla:...)` 或 `(?*...)` - 非原子正向前瞻

- `(*naplb:...)` 或 `(?<*...)` - 非原子正向后顾


## 7. 子程序调用(Subroutine Calls)


将捕获组作为子程序调用。


### 7.1 语法

- `(?&name)` - 调用命名捕获组

- `(?n)` - 调用第n个捕获组

- `(?+n)`, `(?-n)` - 相对调用

- `(?P>name)` - Python语法


**示例:**

- 文本: "abc def ghi"

- 表达式: `(?<word>\w+)\s+(?&word)\s+\w+`

- 预期结果: 匹配"abc def ghi"(如果第二部分是"abc"的话)


## 8. 字符串扫描断言(PCRE2扩展)


检查捕获的子字符串是否符合特定模式。


### 8.1 语法

- `(*scan_substring:(group_numbers_or_names)...)` 或 `(*scs:(...)...)`


## 9. 脚本运行(Script Runs)


确保匹配的字符序列属于同一Unicode脚本。


### 9.1 语法

- `(*script_run:...)` 或 `(*sr:...)` - 确保匹配的字符是同一脚本运行

- `(*atomic_script_run:...)` 或 `(*asr:...)` - 原子脚本运行


## 10. 回溯控制动词(Backtracking Control Verbs)


控制匹配过程中的回溯行为。


### 10.1 立即动作动词

- `(*ACCEPT)` - 立即成功结束匹配

- `(*FAIL)` - 立即失败并触发回溯


**示例:**

- 文本: "abcdef"

- 表达式: `abc(*ACCEPT)xyz`

- 预期结果: 匹配"abc"


### 10.2 回溯后动作动词

- `(*COMMIT)` - 提交匹配点,失败时不尝试其他起始位置

- `(*PRUNE)` - 剪枝,失败时不再回溯到此点之前

- `(*SKIP)` - 跳过,失败时跳到此点位置继续匹配

- `(*THEN)` - 然后,失败时跳到下一个替代项


## 11. 标记(Mark)


记录匹配路径上的标记点。


### 11.1 语法

- `(*MARK:name)` 或 `(*:name)` - 设置标记名称


## 12. 特殊起始项(Special Start-of-Pattern Items)


在模式开头设置选项。


### 12.1 UTF支持

- `(*UTF)` - 启用UTF模式


### 12.2 Unicode属性支持

- `(*UCP)` - 使用Unicode属性进行\w、\d等匹配


### 12.3 行终止符约定

- `(*CR)` - 使用CR作为行终止符

- `(*LF)` - 使用LF作为行终止符

- `(*CRLF)` - 使用CRLF作为行终止符

- `(*ANYCRLF)` - 任何CR、LF或CRLF

- `(*ANY)` - 任何Unicode行终止符

- `(*NUL)` - 使用NUL字符作为行终止符


### 12.4 回溯限制

- `(*LIMIT_HEAP=d)` - 设置堆限制

- `(*LIMIT_MATCH=d)` - 设置匹配限制

- `(*LIMIT_DEPTH=d)` - 设置深度限制


## 13. 内部选项设置


在模式内部设置选项。


### 13.1 Perl兼容选项

- `(?i)` - 不区分大小写

- `(?m)` - 多行模式

- `(?s)` - 单行模式(点号匹配换行符)

- `(?x)` - 扩展模式(忽略空格)

- `(?xx)` - 扩展模式(额外忽略空格)


### 13.2 PCRE2特有选项

- `(?J)` - 允许重复的组名

- `(?U)` - 非贪婪默认

- `(?aD)` - ASCII \d

- `(?aS)` - ASCII \s

- `(?aW)` - ASCII \w

- `(?aP)` - ASCII POSIX类


## 14. 实际应用示例


### 14.1 反向引用示例

- 文本: "sensibility"

- 表达式: `(sens|respons)e and \1ibility`

- 预期结果: 不匹配(单独使用该表达式)

- 文本: "sense and sensibility"

- 表达式: `(sens|respons)e and \1ibility`

- 预期结果: 匹配整个字符串


### 14.2 递归表达式示例

- 文本: "((()))"

- 表达式: `\(([^()]++|(?R))*\)`

- 预期结果: 匹配整个字符串"((()))"


### 14.3 复杂模式示例

- 文本: "192.168.1.1"

- 表达式: `(?(DEFINE)(?<byte>2[0-4]\d|25[0-5]|1\d\d|[1-9]?\d))\b(?&byte)(\.(?&byte)){3}\b`

- 预期结果: 匹配整个IP地址


## 15. 注意事项


1. 反向引用必须引用已经存在的捕获组

2. 在重复组内的反向引用可以实现递归效果

3. 递归表达式可用于匹配任意深度的嵌套结构

4. 使用相对引用有助于编写可重用的正则表达式片段

5. 命名引用提高了正则表达式的可读性和维护性

6. 某些高级功能(如非固定长度后顾断言)在某些PCRE2版本中可能受限

7. 回溯控制动词仅在传统匹配函数中有效,不适用于DFA匹配

8. 某些功能可能需要特定的编译选项才能启用

9. 平衡组功能在我们的COM组件中通过语法转换实现,将.NET语法转换为PCRE2等价形式


这些功能使得PCRE2成为一个功能非常强大的正则表达式引擎,能够处理复杂的文本匹配任务。