基于规则的分词:正则表达式、领域特定规则,中文分词的特殊挑战

说实话,很多人一上来就扑进深度学习的怀抱,觉得用模型分词才是正路。但我在实际项目中踩过的坑告诉我——规则分词这块硬骨头,你绕不过去。数据清洗、日志解析、特定领域术语提取,这些场景下纯规则往往比模型跑得更快、更可控。

什么叫基于规则?说白了就是你告诉程序:看到什么格式就切,看到哪些字必须连在一起。我们今天拆成三块来讲:正则表达式、领域特定规则、以及绕不开的中文特殊挑战。

核心观点:规则分词是“确定性”的——同样的输入永远返回同样的输出。这在生产环境调试时极其重要。

规则分词核心挑战与应对 规则分词 正则表达式匹配 领域特定规则 中文特殊挑战 边界扫描 模式提取与替换 性能陷阱 词典构建 优先级排序 否定规则 歧义消解 未登录词识别 特殊符号处理 规则分词是“确定性”方法,适合调试、日志处理与领域定制场景 但中文的歧义性与未登录词问题,是规则方案最大的敌人

正则表达式——最灵活的文本扫描工具

正则表达式是规则分词的第一把利器。我习惯把它理解为一种模式描述语言:你描述你要找的东西长什么样,程序帮你找到它。

举个简单的例子,我要从一段文本里提取出所有手机号:

import re

text = "我的电话是13812345678,备用号是010-88886666"
pattern = r'1[3-9]\d{9}'
matches = re.findall(pattern, text)
print(matches)  # ['13812345678']

这里的关键在于,\d{9}匹配了9位数字,加上前缀,刚好11位。别小看这个写法,我在处理生产环境日志时经常用这种模式批量切分用户ID和订单号。

我的习惯:写正则前先在Regex101或类似工具上测一遍,尤其是边界情况——比如“号码后面紧跟着汉字”这种。

在实际分词任务中,正则通常扮演三种角色:

  1. 边界分割——用\s+、[,。!]这类模式把文本初步切碎。
  2. 模式提取——把邮箱、URL、金额这类固定格式单独拎出来。
  3. 替换清理——把冗余字符替换成统一分隔符,方便下一步处理。

嗯,这里要提个醒。正则有个毛病:写得太复杂就会变得又难读又慢。我在一次处理亿级日志的分词任务时就吃过亏——正则里套了好多层交替条件,跑了一天才发现是表达式回溯导致的指数级耗时。

避坑指南:我曾经因为嵌套量词导致正则回溯爆炸。比如 (a+)+b 这种模式,对“aaaaaaaaaac”匹配时,引擎会尝试所有可能的分组方式,时间直接崩到O(2ⁿ)。

领域特定规则——让分词更“懂行”

正则虽然灵活,但它不懂业务。举个例子:在金融领域,“上证综指”必须作为一个整体,你不能把“上证”和“综指”拆开。通用分词器做不到这一点,所以你需要领域词典和业务规则。

我个人习惯把领域规则分为三种:

类型 说明 举例
强制保留词 遇到这些词就不切 "机器学习"、"科创板"
强制切分词 遇到这些词必须切 "但是"、"然而"、"以及"
前后缀规则 根据位置决定是否切分 "总"+"经理"→"总经理"

实际构建领域规则时,我通常会走这么几步:

  • 收集领域术语:让业务方提供一份种子词表,少则几百条,多则几万条。
  • 定优先级:长词优先匹配。比如“中华人民共和国”优先级高于“中华”、“人民”、“共和国”。
  • 加否定规则:某些写在规则里的词要在特定上下文下取消。比如“小明在人民广场等”中的“人民广场”应该保留,但“他是人民的好儿子”中“人民”应该独立。
# 伪代码展示领域规则优先级
rules = [
    ("中华人民共和国", MAX_SCORE),    # 最长优先
    ("人民广场", SCORE_HIGH),
    ("人民", SCORE_BASE),             # 短词只有在不被长词覆盖时才生效
]

我在医疗病历分词项目里就这么干过——把“糖尿病肾病”、“高血压性心脏病”这些术语做成保留词,准确率从70%直接提到90%以上。

中文分词的特殊挑战——规则派最难啃的骨头

好,到最难的部分了。中文分词跟英文有个本质区别:英文天然用空格隔开,中文没有。这就产生了几个让规则派头疼的问题。

第一个是歧义问题。经典的例子是:

"南京市长江大桥"
可能切法1: 南京市 / 长江大桥
可能切法2: 南京市长 / 江大桥

为什么会有歧义?因为“南京市”和“南京市长”在词典里同时存在,“长江大桥”和“江大桥”也是。程序需要做决策——这是规则分词的天花板。

我常用的做法是双向最大匹配:从左往右切一次(取最长词),从右往左切一次(也取最长词),然后选成片数量少的那一种。比如“南京市长江大桥”,正向匹配会切成“南京市 / 长江大桥”(2个词),逆向匹配会切成“南 / 京市 / 长江大桥”(3个词),我们选前者。

经验之谈:双向最大匹配只能解决部分交叉歧义,碰到组合歧义(比如“把手弄脏了”的“把手”可以是动词也可以是名词),还是得靠统计或上下文。

第二个是未登录词问题。新出现的网络流行语、罕见的专业术语、人名地名——这些不在词典里,规则根本不知道它是词。我最痛苦的一次是处理用户评论数据,“绝绝子”、“YYDS”这种新词全被切成单字,结果做出来的标签云全是单字符。

第三个是特殊符号的处理。中文全角半角混用、数字和汉字连写(“3D打印”、“5G网络”)、英文缩写的分词边界——这些都让规则变得臃肿不堪。

拿我自己的体会来说,中文分词的规则难写在哪?它不像英文那么“规整”。英文里\b\w+\b基本能搞定大部分场景。中文你得一个字一个字地去猜哪些字该凑在一起。下面这个表格你看看对比就明白了:

维度 英文分词 中文分词
分隔符 天然存在(空格) 无天然分隔符
词长 词长基本固定 词长变化大(1~10+字)
歧义率 较低 高,尤其短文本
新词出现 少,且通常有词缀 频繁,无固定规律
规则复杂度 中 极高,容易规则冲突

面对中文的特殊挑战,纯规则方案很难达到95%以上的准确率。我自己在实际项目中,通常把规则分词当作预切分阶段,先排除掉确定性的模式(手机号、身份证、专有名词),再把模糊区域交给统计模型去解决。说白了就是——规则兜底,模型冲锋。

给你的建议:如果你刚接触规则分词,别想一步到位。先从一种场景出发——比如先写好一个提取手机号/邮箱的正则,再慢慢加入领域词典。我每次迭代都不会超过50条规则,多了就重构。