返回
创建于
状态公开

核心概念

  • 单词边界 \b:介于“字母/数字/下划线\w)”与“\w”之间的位置。

    • 多数引擎里 \wASCII[A-Za-z0-9_](JS、Python 默认如此)。中文、日文、韩文都不算 \w,因此 \b 在 CJK 旁边经常“失灵”。
  • 环视(Lookaround):零宽断言,不消费字符。

    • 正向肯定 (?=...)正向否定 (?!...)
    • 反向肯定 (?<=...)反向否定 (?<!...)

什么时候该用哪一个?

  • 仅英文/数字词:cat 独立出现 → \bcat\b
  • 中英混排或含中文:不要依赖 \b,用 Unicode 属性 + 环视 更可靠。
  • 需要“前后不属于某类字符” → 两侧各用一次(负)环视。

常见语言支持差异

  • JavaScript\b 基于 ASCII;支持 lookbehind(现代引擎已支持)。

    • /uUnicode 属性类\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,不匹配 concatenatebobcat
    • 变体(不区分大小写):/\bcat\b/i

CJK 与混排场景:不要依赖 \b

1) 匹配“汉字词”本身

  • JS(推荐,需 u 标志)

    js
    1// 一个或多个汉字
    2const re = /\p{Script=Han}+/u
  • 控制“左右不是汉字”(避免吞并):

    js
    1// 左右都不是汉字,或已到字符串边界
    2const re = /(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})/u

2) 匹配“英文/数字词”且不粘中文

  • JS

    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 友好)

  • 定义“词字符”= 字母或数字或下划线或汉字:

    js
    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”这个词,左右不连着词字符/汉字):

    js
    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
  • 示例(粗略思路):

    go
    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 友好):

    js
    1const re = /(?<![\p{L}\p{N}_\p{Script=Han}])[A-Za-z]+(?![\p{L}\p{N}_\p{Script=Han}])/u

配方 C:只匹配汉字词,且不吞并相邻汉字

  • JS:

    js
    1const re = /(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})/u

配方 D:匹配“电子邮件”但不粘连中文

  • JS(示意):

    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

常见坑与规避

  1. \b 在中文边上不工作:因为 \w 不含 CJK。中英混排请改用环视 + Unicode 属性类。
  2. JS 忘了 u 标志\p{...} 只有在 u 下才生效。
  3. Go 不支持 lookbehind:改写为分支+捕获组逻辑,或在上游/下游做切分。
  4. Python 内置 re 不支持 \p{...}:用第三方 regex 包(pip install regex)并加 flags=regex.UNICODE
  5. 标点与全角半角[\p{L}\p{N}_] 不含标点,若希望把特定符号当成“词内字符”,需显式加入。
  6. 性能:多重环视在大文本上可能慢,能预分词/预切块就预处理。

快用示例(JS)

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:英文整词

文本:

js
1The cat is sleeping. A bobcat is running.

目标: 匹配单独的 cat,不匹配 bobcat

  • \b

    regex
    1\bcat\b

    命中:cat 不命中:bobcat


🧩 示例 2:英文夹中文

文本:

js
1你好cat世界

目标: 匹配 cat,但不要把它当作中文词的一部分。

  • \b

    regex
    1\bcat\b

    失败(JS/Python 默认 \b 只看 ASCII,你好cat 前后的中文不是 \w,边界判断错误)。

  • 推荐(JS,带 u 标志):

    regex
    1(?<![\p{L}\p{N}_\p{Script=Han}])cat(?![\p{L}\p{N}_\p{Script=Han}])

    ✅ 正确命中 cat


🧩 示例 3:只取独立中文词

文本:

js
1abc你好世界def

目标: 抽取出“你好世界”这段汉字。

  • 推荐(JS):

    regex
    1(?<!\p{Script=Han})\p{Script=Han}+(?!\p{Script=Han})

    命中:你好世界


🧩 示例 4:避免相连数字

文本:

js
1price123 not1234 456price

目标: 匹配单独的 123,但不匹配 1234 或嵌在其他词中的。

  • \b

    regex
    1\b123\b

    命中:123 不命中:1234456price


🧩 示例 5:邮箱检测避免中文粘连

文本:

js
1我的邮箱是abc@test.com谢谢

目标: 提取 [email protected],确保不和中文粘连。

  • 推荐(JS,Unicode 属性):

    regex
    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}])

    命中:[email protected]


🧩 示例 6:Go 不支持 lookbehind

文本:

js
1hello cat dog

目标: 匹配独立的 cat

  • Go 替代方案:

    regex
    1(?:^|[^A-Za-z0-9_])(cat)(?![A-Za-z0-9_])

    然后取捕获组 1,得到 cat


总结

  • \b 适合纯英文/数字场景,快捷简单。
  • 环视更灵活,尤其在 CJK(中文/日文/韩文)或混排文本时。
  • Go 不支持反向环视 → 需改写成“分支 + 捕获组”模式。