边界是什么
单词边界是一个零宽断言:它不匹配字符,只判断当前位置的左右两侧是不是「一边属于单词字符、另一边不属于」。只要这个条件成立,边界就成立,所以它的落点始终在字符之间的缝隙上。
单词字符的范围决定一切
边界是否成立,取决于引擎对单词字符的定义。ASCII 语义下只有字母数字与下划线算单词字符;Unicode 语义下绝大多数文字都算。因此同一个模式在 Python 与 JavaScript 里对中文、带音标字母的表现可能完全不同。
基本信息
| 写法 | 反斜杠加小写 b 表示边界,大写 B 表示非边界 |
|---|---|
| 宽度 | 零宽,不消耗字符,不出现在结果里 |
| 判定依据 | 左右两侧是否跨越「单词字符与非单词字符」的分界 |
| 串首串尾 | 串首若紧邻单词字符则边界成立,串尾同理 |
| 引擎差异 | Python 处理文本字符串时默认 Unicode 感知;JavaScript 默认按 ASCII 处理,除非使用 Unicode 字母属性类 |
| 命令行等价 | grep 的整词匹配选项与单词边界效果相近,但边界定义由工具实现决定 |
典型使用场景
- 精确匹配某个英文单词,不让它命中更长单词里的一部分
- 改写标识符:把变量名整体替换,同时保留其前后缀
- 在日志里统计某关键词出现次数,避免子串误计
- 在模式里区分「独立出现的单位」与「作为前缀的一部分」
- 处理中英混排文本时判断边界是否真的可用,必要时改用字符类
边界的写法与落点
边界的写法与落点
import re
re.findall(r'\bcat\b', 'cat concatenate cat.') # 只命中独立单词
re.findall(r'\Bcat', 'concatenate') # 非边界:命中单词内部
re.sub(r'\bfoo\b', 'bar', 'foo foobar') # 只替换独立出现的词
re.findall(r'\b\d+\b', 'a12 345') # 独立的数字串
re.findall(r'\b中\b', '中文 abc') # 中文里的边界判定与语种设定相关,勿直接依赖
re.findall(r'(?u)\b\w+\b', 'héllo 世界') # Unicode 语义下的单词字符边界行为对照
| 位置 | 是否边界 | 说明 |
|---|---|---|
| 空格与字母之间 | 是 | 最常见的边界 |
| 字母与数字之间 | 否 | 两者都算单词字符 |
| 下划线与字母之间 | 否 | 下划线算单词字符 |
| 字母与标点之间 | 是 | 标点不属于单词字符 |
| 汉字与字母之间 | 取决于引擎语义 | ASCII 语义下成立,Unicode 语义下不成立 |
单词边界不能替代行锚点。它只表达「这里不是单词中间」,行首行尾的约束仍要另写锚点;两者混用时最容易出现的错误是把 当成了字符串开头。
匹配中文或其它非拉丁文字时,不要依赖单词边界做分词式的切分。更稳妥的做法是用明确的字符类把目标字与上下文写清楚,或先把文本按标点切分再逐段处理。