首先 记住一个要点:出现
<就看左边,没有<就是看右边。?=引入"断言",我们的目标是提取"断言"前或者后方的字符
0️⃣ 示例:从句子中提取最后一个价格
r
text="The prices are $120, $45 and $300 today."
使用
str_extract_all(text,pattern = "\\d+")能提取所有数字,但是要提取最后一个价格$300,如何只提取满足特定条件(如在$后面、或者,前面)?这就需要 PCRE 的 lookaround(零宽断言) 技巧,R和Python基本通用。
1,2节为PCRE简介,可以跳过阅读
1️⃣ PCRE 简介
PCRE = Perl Compatible Regular Expressions(Perl 兼容正则表达式)
- 不是语言,而是 正则表达式引擎规则 :
- 支持特殊符号、分组、反向引用、lookaround 等高级功能
- 控制字符串匹配方式
- 可理解为:"正则表达式的标准语法"
| 是否使用 PCRE | 语言/工具 |
|---|---|
| ✔ 使用 | R 的 stringr/stringi |
| ✔ 使用 | PHP(preg 系列) |
| ✔ 使用 | VSCode、grep -P |
| ✘ 不使用 PCRE(类似) | Python re(独立实现,但语法 90% 接近) |
| ✘ 不使用 PCRE | JavaScript 正则(ECMA262 标准) |
2️⃣ R 与 Python 正则对比
2.1 提取匹配函数
| 功能 | R:stringr | Python:re |
|---|---|---|
| 单个匹配 | str_extract() |
re.search().group() |
| 全部匹配 | str_extract_all() |
re.findall() |
| 是否 PCRE 风格 | ✔ | ✔(语法基本一致) |
关键差异
- R 字符串中
\需写成\\ - Python 原生字符串
r""可直接写\
学会一次,R/Python 都可用。
3️⃣ lookaround 四种零宽断言
3.1 四种写法与记忆规律
| 类型 | 英文名称 | 语法 | 描述说明 |
|---|---|---|---|
| 正向前瞻 | Lookahead | XXXXX(?=...) |
检查当前位置右边 是否满足条件,提取前面的字符 |
| 正向后顾 | Lookbehind | (?<=...)XXXXX |
检查当前位置左边 是否满足条件,提取后面的字符 |
| 负向前瞻 | Negative Lookahead | XXXXX(?!...) |
检查右边不满足条件 |
| 负向后顾 | Negative Lookbehind | (?<!...)XXXXX |
检查左边不满足条件 |
规律总结:
?:引入断言=:等于 (满足条件)!:非 (不满足条件)<:左边(后顾),没有<则默认右边(前瞻)
4️⃣ lookaround 基本示例
示例句子
text
"The prices are $120, $45 and $300 today."
4.1 提取 $ 后所有数字(正向后顾,Lookbehind)
r
stringr::str_extract_all(text, "(?<=and\\s\\$)\\d+")
python
re.findall(r"(?<=and\s\$)\d+", text)
输出 :['300']
向左看是 "and空格$" ,则提取后方的数字
4.2 提取非 $ 后数字(负向后顾, Neg Lookbehind)
r
str_extract_all(text, "(?<!\\$)\\d+")
python
re.findall(r"(?<!\$)\d+", text)
输出 :['20', '5', '00']
向左看不是$,则提取后方的数字。
4.3 正向前瞻 (?=...) (Lookahead)
r
str_extract_all(text, "\\d+(?=\\s)")
python
re.findall(r"\d+(?=\s)", text)
输出 :['45', '300']
向右看是空格,则提取前方的数字。
4.4 负向前瞻 (?!...) (Neg Lookahead)
r
str_extract_all(text, "\\d+(?![\\s,])")
python
re.findall(r"\d+(?![\s,])", text)
输出 :['12', '4', '30']
向右看不是空格也不是逗号,取其前方的数字
5️⃣ R 中使用 PCRE 注意事项
- base R 函数(
grep,gsub,sub,grepl)默认不支持 lookaround - 使用 PCRE 扩展语法需加
perl=TRUE
| 使用方式 | 是否需要 perl=TRUE | 引擎 |
|---|---|---|
| base R | 是 | POSIX → PCRE |
| stringr/stringi | 否 | ICU(支持) |
6️⃣ 实战示例集合
注意!R语言中是双斜杠进行转义
| 任务 | 示例文本 | 正则 | 输出示例 |
|---|---|---|---|
提取 email @ 前用户名 |
"Contact us at alice@example.com or bob.smith@domain.org." |
[^\\s@]+(?=@) |
['alice','bob.smith'] |
| 基因名后数字 | "Gene expression: TP53_123, BRCA1_45, MYC_300." |
(?<=_)[0-9]+ |
['123','45','300'] |
提取 $ 后数字(保留小数点) |
"Prices: $199, $20.5, 30.0 USD." |
(?<=\\$)\d+(?:\\.\\d+)? |
["199","20.5]" |
| 提取 URL 最后文件名 | "URLs: http://example.com/data/file.txt and https://site.org/index.html" |
(?<=/)[^/]+$ |
['index.html'] |
| 提取 hashtags | "Hashtags: #fun #learning #RStats" |
(?<=#)\w+ |
['fun','learning','RStats'] |
排除 $ / 括号 / 基因编号 |
"Mixed: $100, 200, (300), geneX_500, email@test.com" |
(?<!\$)(?<!\()\b\d+\b |
['200'] |
\b和\w详见 附录:单词边界 的介绍
🔹 总结
- 四种 lookaround 的记忆规律:
?=:右边等于 → 正向前瞻?<=:左边等于 → 正向后顾?!:右边非 → 负向前瞻?<!:左边非 → 负向后顾- 不用管什么正负前后,只要
<就是在左边,没有<就是在右边
- R/Python 正则基本一致,R 注意
\\转义 - lookaround 零宽,匹配位置不消耗字符,可精确控制条件。
- **注意!**R语言中 PCRE lookbehind 的长度必须可预估 ,不能将
.*这种任意长度的模式放在结构中,如(?<!\(\.*)\d+(?!\.*\))会报错。Python的regex模块支持可变长度。
附录:单词边界
1️⃣ 单词字符 \w
- 匹配 字母、数字或下划线
[A-Za-z0-9_]
2️⃣ 单词边界 \b
- 位置,不是字符
- 匹配单词开始或单词结束的位置
- 条件:左右一边是单词字符,一边是非单词字符或字符串开头/结尾
3️⃣ 示例:"abc 123 _500 $45"
标注 | 表示字符间的位置:
|a|b|c| |1|2|3| | _ |5|0|0| |$|4|5|
"abc":|a✅ 左边非单词字符(开头) → 边界c|✅ 右边空格 → 边界b|c❌ 两边都是单词字符 → 不是边界
"123":|1✅ 左边空格 → 边界3|✅ 右边空格 → 边界
"_500":_ |5❌ 左边_是单词字符 → 不是边界5|❌ 右边0是单词字符 → 不是边界
"$45":$|4✅ 左边$非单词字符 → 边界5|✅ 右边空格 → 边界
4️⃣ 总结
\w→ 单词字符(字母/数字/下划线)\b→ 单词边界(单词开始或结束位置)\b\d+\b→ 只匹配独立数字 ,即两边均是单词边界的多个数值,不会匹配abc,_500或$45,而是123,500和45