核心概念
-
单词边界
\b:介于“字母/数字/下划线(\w)”与“非\w”之间的位置。- 多数引擎里
\w是 ASCII:[A-Za-z0-9_](JS、Python 默认如此)。中文、日文、韩文都不算\w,因此\b在 CJK 旁边经常“失灵”。
- 多数引擎里
-
环视(Lookaround):零宽断言,不消费字符。
- 正向肯定
(?=...)、正向否定(?!...) - 反向肯定
(?<=...)、反向否定(?<!...)
- 正向肯定
什么时候该用哪一个?
- 仅英文/数字词:
cat独立出现 →\bcat\b - 中英混排或含中文:不要依赖
\b,用 Unicode 属性 + 环视 更可靠。 - 需要“前后不属于某类字符” → 两侧各用一次(负)环视。
常见语言支持差异
-
JavaScript:
\b基于 ASCII;支持 lookbehind(现代引擎已支持)。- 用
/u与 Unicode 属性类:\p{L}(字母),\p{N}(数字),\p{Script=Han}(汉字)。
- 用
-
Python:
\b也偏 ASCII 语义;regex第三方库支持更强;内置re无\p{...}属性(需regex库或手动范围)。 -
Go:不支持 lookbehind(只支持 lookahead)。需要改写成等价前瞻或分组方案。
-
PCRE / Java / .NET:普遍支持 lookbehind 与 Unicode 属性类(开启相应标志)。
英文词边界(纯英文)
-
JS / Python / PCRE:
\bcat\b—— 匹配cat,不匹配concatenate、bobcat- 变体(不区分大小写):
/\bcat\b/i
CJK 与混排场景:不要依赖 \b
1) 匹配“汉字词”本身
-
JS(推荐,需
u标志):1// 一个或多个汉字 2const re = /\p{Script=Han}+/u -
控制“左右不是汉字”(避免吞并):
1// 左右都不是汉字,或已到字符串边界 2const re = /(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})/u
2) 匹配“英文/数字词”且不粘中文
-
JS:
1// 左右都不是字母/数字/下划线,也不是汉字 2const re = /(?<![\p{L}\p{N}_\p{Script=Han}])[A-Za-z0-9_]+(?![\p{L}\p{N}_\p{Script=Han}])/u解释:用 Unicode 属性把“单词成分”统一起来,避免
\b的 ASCII 限制。
3) “词边界”通用模板(Unicode 友好)
-
定义“词字符”= 字母或数字或下划线或汉字:
1const wordChar = /[\p{L}\p{N}_\p{Script=Han}]/u 2// 词起始:前面不是词字符,当前位置开始为词字符 3// 使用时需把 wordChar 拼进式子: 4const re = new RegExp(String.raw`(?<![\p{L}\p{N}_\p{Script=Han}])目标(?=[\p{L}\p{N}_\p{Script=Han}])?`, 'u') -
直接写完整例子(匹配“AI”这个词,左右不连着词字符/汉字):
1const re = /(?<![\p{L}\p{N}_\p{Script=Han}])AI(?![\p{L}\p{N}_\p{Script=Han}])/u
没有 lookbehind 的语言如何改写(如 Go)
-
反向环视
(?<!X)可改写为 捕获组 + 选择:- 目标:匹配不被
X紧贴在左侧的T - 等价思路:匹配“开头处的
T”或“前面有一个非X的任意字符再跟T”,然后只取T。
- 目标:匹配不被
-
示例(粗略思路):
1// Go 不支持 \p{...} 属性类;可用手动范围或第三方库 2// 用两个分支模拟“左侧不是词字符” 3// (^|[^A-Za-z0-9_]) (T) -> 提取第二组即 T 4re := regexp.MustCompile(`(?:^|[^A-Za-z0-9_])(cat)(?![A-Za-z0-9_])`) 5// 注意:需要用分组结果而不是整体匹配
典型需求配方
配方 A:纯英文“整词”匹配
- JS/Python:
/\bserver\b/i
配方 B:英文词但两侧可能是中文或标点
-
JS(Unicode 友好):
1const re = /(?<![\p{L}\p{N}_\p{Script=Han}])[A-Za-z]+(?![\p{L}\p{N}_\p{Script=Han}])/u
配方 C:只匹配汉字词,且不吞并相邻汉字
-
JS:
1const re = /(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})/u
配方 D:匹配“电子邮件”但不粘连中文
-
JS(示意):
1const re = /(?<![\p{L}\p{N}_\p{Script=Han}])[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}(?![\p{L}\p{N}_\p{Script=Han}])/u
常见坑与规避
\b在中文边上不工作:因为\w不含 CJK。中英混排请改用环视 + Unicode 属性类。- JS 忘了
u标志:\p{...}只有在u下才生效。 - Go 不支持 lookbehind:改写为分支+捕获组逻辑,或在上游/下游做切分。
- Python 内置
re不支持\p{...}:用第三方regex包(pip install regex)并加flags=regex.UNICODE。 - 标点与全角半角:
[\p{L}\p{N}_]不含标点,若希望把特定符号当成“词内字符”,需显式加入。 - 性能:多重环视在大文本上可能慢,能预分词/预切块就预处理。
快用示例(JS)
1// 1) 纯英文整词
2/\bcat\b/i
3
4// 2) 英文词(左右不可连字母/数字/下划线/汉字)
5/(?<![\p{L}\p{N}_\p{Script=Han}])[A-Za-z]+(?![\p{L}\p{N}_\p{Script=Han}])/u
6
7// 3) 单个或一段汉字,且左右不是汉字
8/(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})/u
9
10// 4) “AI”独立出现(不粘连中英文/数字/下划线/汉字)
11/(?<![\p{L}\p{N}_\p{Script=Han}])AI(?![\p{L}\p{N}_\p{Script=Han}])/u🧩 示例 1:英文整词
文本:
1The cat is sleeping. A bobcat is running.目标: 匹配单独的 cat,不匹配 bobcat。
-
用
\b:1\bcat\b命中:
cat不命中:bobcat
🧩 示例 2:英文夹中文
文本:
1你好cat世界目标: 匹配 cat,但不要把它当作中文词的一部分。
-
用
\b:1\bcat\b❌ 失败(JS/Python 默认
\b只看 ASCII,你好cat前后的中文不是\w,边界判断错误)。 -
推荐(JS,带
u标志):1(?<![\p{L}\p{N}_\p{Script=Han}])cat(?![\p{L}\p{N}_\p{Script=Han}])✅ 正确命中
cat
🧩 示例 3:只取独立中文词
文本:
1abc你好世界def目标: 抽取出“你好世界”这段汉字。
-
推荐(JS):
1(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})命中:
你好世界
🧩 示例 4:避免相连数字
文本:
1price123 not1234 456price目标: 匹配单独的 123,但不匹配 1234 或嵌在其他词中的。
-
用
\b:1\b123\b命中:
123不命中:1234、456price
🧩 示例 5:邮箱检测避免中文粘连
文本:
1我的邮箱是abc@test.com谢谢目标: 提取 [email protected],确保不和中文粘连。
-
推荐(JS,Unicode 属性):
1(?<![\p{L}\p{N}_\p{Script=Han}])[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}(?![\p{L}\p{N}_\p{Script=Han}])
🧩 示例 6:Go 不支持 lookbehind
文本:
1hello cat dog目标: 匹配独立的 cat。
-
Go 替代方案:
1(?:^|[^A-Za-z0-9_])(cat)(?![A-Za-z0-9_])然后取捕获组 1,得到
cat。
总结
\b适合纯英文/数字场景,快捷简单。- 环视更灵活,尤其在 CJK(中文/日文/韩文)或混排文本时。
- Go 不支持反向环视 → 需改写成“分支 + 捕获组”模式。