Hunspell 拼写检查完全教程 / 第 05 章:词缀规则详解
第 05 章:词缀规则详解
5.1 词缀规则概述
词缀规则(Affix Rules)是 Hunspell 的核心机制。它允许从少量词根生成大量词形,极大压缩词典体积。
词根: "run" → 运行 SFX 规则 → run, runs, running, ran
词根: "happy" → 运行 SFX 规则 → happy, happier, happiest, happily, happiness
词根: "unhappy" → 运行 PFX+SFX → unhappy, unhappier, unhappiest, unhappily
每条规则由以下部分组成:
[类型] [标志] [可组合] [条目数]
[类型] [标志] [条件] [删除] [添加] [匹配条件]
5.2 SFX(后缀规则)
5.2.1 语法格式
SFX <标志> <可组合(Y/N)> <条目数>
SFX <标志> <删除> <添加> [匹配条件]
| 字段 | 说明 |
|---|---|
| 标志 | 单个标志字符(如 D、S、G) |
| 可组合 | Y = 可与其他 SFX 组合;N = 不可组合 |
| 条目数 | 后续规则行的数量 |
| 删除 | 从词根末尾删除的字符(0 表示不删除) |
| 添加 | 添加到词根末尾的字符(0 表示不添加) |
| 匹配条件 | 正则表达式,匹配词根的末尾字符(可选) |
5.2.2 英语过去式规则详解
SFX D Y 4
SFX D 0 d e
SFX D 0 ed [^e]
SFX D y ied [^aeiou]y
SFX D 0 ed [aeiou]y
逐行解析:
规则 1: SFX D 0 d e
含义: 如果词根以 'e' 结尾,删除 ''(不删除),添加 'd'
示例: love → love + d = loved
dance → dance + d = danced
规则 2: SFX D 0 ed [^e]
含义: 如果词根不以 'e' 结尾,删除 '',添加 'ed'
示例: walk → walk + ed = walked
play → play + ed = played(注意:此规则也被 [aeiou]y 匹配,但规则3优先)
规则 3: SFX D y ied [^aeiou]y
含义: 如果词根以 辅音+y 结尾,删除 'y',添加 'ied'
示例: try → tr + ied = tried
carry → carr + ied = carried
规则 4: SFX D 0 ed [aeiou]y
含义: 如果词根以 元音+y 结尾,删除 '',添加 'ed'
示例: play → play + ed = played
enjoy → enjoy + ed = enjoyed
规则匹配顺序:Hunspell 从第一条规则开始尝试,第一条匹配的规则生效。
5.2.3 英语复数规则详解
SFX S Y 3
SFX S 0 s [^sxzh]
SFX S 0 es [sxzh]
SFX S y ies [^aeiou]y
规则 1: cat → cats, dog → dogs, book → books
规则 2: box → boxes, church → churches, wish → wishes, buzz → buzzes
规则 3: fly → flies, city → cities, baby → babies
例外(需要手动在 .dic 中处理):
child → children(不规则,直接写 child/SM 中 S 标志不对,需要手动处理)
mouse → mice(不规则,直接在 .dic 中列出)
5.2.4 现在分词规则详解
SFX G Y 4
SFX G e ing e
SFX G 0 ing [^e]
SFX G y ying [aeiou]y
SFX G 0 ing [^aeiou][aeiou][^aeiou]
规则 1: make → mak + ing = making, dance → danc + ing = dancing
规则 2: walk → walk + ing = walking, run → run + ing = running
规则 3: play → play + ing = playing(元音+y 保持 y)
规则 4: swim → swim + ing = swimming(单音节 CVC 结构双写末辅音)
注意:规则 4 的 [^aeiou][aeiou][^aeiou] 匹配"辅音+元音+辅音"结构,
只适用于重读闭音节。规则简化处理,部分不规则需手动处理。
5.2.5 组合标志示例
当一个词根有多个标志时,每个标志独立展开:
# .dic 文件
run/DGS
# 展开结果:
# D: run → ran(不规则,这里只是示意;实际需要额外处理)
# G: run → running
# S: run → runs
# D+G: 一般不组合
# 所有组合:run, runs, running, ran(ran 需要手动处理或用 PFX 规则)
5.2.6 可组合标志(Y/N)
# Y = 可与其他后缀组合
SFX D Y 4 # 过去式可与其他后缀组合
SFX D 0 d e # loved → lovedly? 不常见,但结构上允许
# N = 不可与其他后缀组合
SFX S N 3 # 复数不再与后缀组合
SFX S 0 s [^sxzh] # cats → cats,不会继续加后缀
实际使用中,Y 主要用于动词词缀(可组合 -ly, -ness 等),N 用于名词/形容词词缀。
5.3 PFX(前缀规则)
5.3.1 语法格式
PFX <标志> <可组合(Y/N)> <条目数>
PFX <标志> <删除> <添加> [匹配条件]
与 SFX 完全对称,只是作用于词根的开头。
5.3.2 英语否定前缀 un-
PFX U Y 1
PFX U un 0 .
解析:
标志: U
可组合: Y
条目数: 1
删除: un
添加: 0(不添加前缀)
条件: .(匹配任意词根)
效果:
unhappy → happy(去前缀 un)
undo → do(去前缀 un)
注意:这是"反向"规则,用于从派生词找到词根。
但实际使用时,词根 happy 带 U 标志:
.dic: happy/UR
则 happy 会通过 PFX U 规则生成 unhappy
5.3.3 英语否定前缀系列
PFX U Y 2
PFX U un 0 .
PFX I Y 2
PFX I in 0 .
PFX I im 0 [bmp] # impossible, immobile, irregular
PFX N Y 1
PFX N non 0 .
PFX M Y 1
PFX M mis 0 .
PFX D Y 1
PFX D dis 0 .
PFX R Y 1
PFX R re 0 .
# .dic 文件使用
happy/UIR # happy → unhappy (U), 不变 (I), happy → rehappy? (R)
possible/IR # possible → impossible (I), repossible? (R)
spell/MR # spell → misspell (M), respell (R)
5.3.4 语序问题
前缀和后缀的应用顺序:先应用前缀,再应用后缀。
unhappy + -ness:
Step 1: PFX U → un + happy = unhappy
Step 2: SFX N → unhappy + ness = unhappiness
但 unhappiness 在词典中需要:
happy/UNR # U=un-前缀, N=-ness后缀, R=re-前缀
5.4 条件替换(Conditional Replacement)
条件替换是 Hunspell 的独特功能,允许在添加词缀的同时修改词根。
5.4.1 替换原理
标准规则: 词根 + 删除 + 添加
条件替换: 词根匹配条件 → 删除词根末尾 → 添加新字符
5.4.2 英语 -able/-ible 规则
SFX B Y 4
SFX B 0 able [^aeiou] # 以辅音结尾:work → workable
SFX B e able e # 以 e 结尾:love → lovable
SFX B 0 able [aeiou] # 以元音结尾:enjoy → enjoyable
SFX B 0 ible .* # 通用:access → accessible(需手动判断)
5.4.3 西班牙语动词变位示例
# 西班牙语 -ar 动词(hablar, 说)
SFX A Y 6
SFX A ar o ar # hablo(我)
SFX A ar as ar # hablas(你)
SFX A ar a ar # habla(他/她)
SFX A ar amos ar # hablamos(我们)
SFX A ar áis ar # habláis(你们)
SFX A ar an ar # hablan(他们/她们)
# 西班牙语 -er 动词(comer, 吃)
SFX E Y 6
SFX E er o er # como
SFX E er es er # comes
SFX E er e er # come
SFX E er emos er # comemos
SFX E er éis er # coméis
SFX E er en er # comen
5.4.4 法语动词变位示例
# 法语 -er 动词现在时(parler, 说)
SFX A Y 6
SFX A er e er # je parle
SFX A er es er # tu parles
SFX A er e er # il/elle parle
SFX A er ons er # nous parlons
SFX A er ez er # vous parlez
SFX A er ent er # ils/elles parlent
# 法语阴性词尾
SFX F Y 2
SFX F e e e # 以 e 结尾不变(如 jaune 阴性仍为 jaune)
SFX F 0 e [^e] # 以非 e 结尾加 e(如 grand → grande)
5.4.5 德语名词复数规则
# 德语名词复数(多种模式)
SFX P Y 5
SFX P 0 e [^aeiou]hlnr # Stuhl → Stühle(需要 umlaut)
SFX P 0 \u00FCe [aou]hlnr # 对应 umlaut 变化
SFX P 0 er [^aeiou] # Kind → Kinder
SFX P 0 n e # Lampe → Lampen
SFX P 0 en [^e] # Tisch → Tische
5.5 跨语言词缀规则对比
5.5.1 各语言形态复杂度
| 语言 | 词缀类型 | 特殊难点 |
|---|---|---|
| 英语 | 后缀为主 | 不规则动词(go→went)需手动列出 |
| 德语 | 后缀+前缀+复合 | 元音变音(Umlaut)、格变化 |
| 法语 | 后缀+前缀 | 动词变位丰富、性数一致 |
| 西班牙语 | 后缀为主 | 动词变位极其丰富(14 时态) |
| 俄语 | 后缀为主 | 6 种格变化、3 种性 |
| 匈牙利语 | 后缀为主 | 18 种格后缀、极度黏着 |
| 芬兰语 | 后缀为主 | 15 种格、高度屈折 |
| 土耳其语 | 后缀为主 | 元音和谐、极度黏着 |
| 阿拉伯语 | 词根模板 | 三辅音词根模式 |
| 日语 | 助词 | 动词活用形变化 |
5.5.2 匈牙利语后缀系统示例
# 匈牙利语格后缀(以 ház/house 为例)
SFX C Y 6
SFX C 0 ban [^aáeéiíoóöőuúüű] # házban (inessive)
SFX C 0 ból [^aáeéiíoóöőuúüű] # házból (elative)
SFX C 0 ba [^aáeéiíoóöőuúüű] # házba (illative)
SFX C 0 nak [^aáeéiíoóöőuúüű] # háznak (dative)
SFX C 0 val [^aáeéiíoóöőuúüű] # házzal (instrumental, 同化 z+v=z)
SFX C 0 t [^aáeéiíoóöőuúüű] # háztól (ablative)
# 匈牙利语的元音和谐规则使后缀选择取决于词根元音
# 后元音词: ház → házban
# 前元音词: kert → kertben
5.5.3 阿拉伯语词根模板
# 阿拉伯语基于三辅音词根的模板系统
# 词根 k-t-b (写) 可生成:
# kitāb (书) - 模式 CiCāC
# kātib (作者) - 模式 CāCiC
# maktab (办公室) - 模式 maCCaC
# kataba (他写了) - 模式 CaCVCV
# Hunspell 对此支持有限,通常需要:
# 1. 枚举所有模板
# 2. 使用大量手动词干条目
# 3. 或使用其他工具(如 qutrub)
5.5.4 俄语名词变格示例
# 俄语阳性名词第一格变格(以 стол/stol 为例)
# стол (table) - 第一变格法
SFX G Y 6
SFX G 0 а . # стол → стола (属格)
SFX G 0 у . # стол → столу (与格)
SFX G 0 ом . # стол → столом (工具格)
SFX G 0 е . # стол → столе (前置格)
SFX G 0 ы [^гжкхцчшщ] # стол → столы (复数主格)
SFX G 0 и [гжкхцчшщ] # парк → парки (复数主格)
# 俄语阴性名词(以 книга/kniga 为例)
SFX F Y 6
SFX F а и а # книга → книги (属格)
SFX F а е а # книга → книге (与格)
SFX F а у а # книгу (宾格)
SFX F а ой а # книгой (工具格)
SFX F а е а # книге (前置格)
SFX F а и а # книги (复数)
5.6 高级词缀技术
5.6.1 CIRCUMFIX(前后缀同时)
# CIRCUMFIX 指定前后缀必须同时出现
# 德语完成时态 ge- + -t
CIRCUMFIX C
SFX C Y 1
SFX C en t en # machen → gemacht(ge- + mach + -t)
PFX C Y 1
PFX C ge 0 .
# .dic 文件中
machen/C # 必须同时有 ge- 前缀和 -t 后缀 → gemacht
5.6.2 NEEDAFFIX(必须有词缀)
NEEDAFFIX N
# .dic 文件中
un/N # "un" 不能独立存在,必须与其他规则组合
dis/N # "dis" 不能独立存在
# 效果:
# 单独检查 "un" → 标记为错误
# "undo" 中的 "un-" 前缀 → 允许(因为 do 提供了词干)
5.6.3 ONLYINCOMPOUND(仅复合词中出现)
ONLYINCOMPOUND O
# .dic 文件中
haus/O # "haus" 只能在复合词中使用,不能独立存在
buch/O # "buch" 只能在复合词中使用
# 单独检查 "haus" → 错误
# 复合词 "schulhaus" → 正确
5.6.4 FORCEUCASE(强制大写)
FORCEUCASE 1
# 用于语言代码标记等特殊场景
# .dic 文件中
i/1 # "I" 必须大写(英语人称代词)
5.6.5 KEEPCASE(保持大小写)
KEEPCASE K
# .dic 文件中
iPhone/K # 必须保持 "iPhone" 大小写
macOS/K # 必须保持 "macOS"
McDonald/K # 必须保持 "McDonald"
# 效果:
# iPhone → 正确 ✓
# iphone → 错误 ✗
# IPHONE → 错误 ✗
5.6.6 NOSUGGEST(不提供建议)
NOSUGGEST X
# .dic 文件中
# 避免敏感词出现在建议列表
damn/X
hell/X
bastard/X
# 效果:
# 检查 "dam" → 会建议 "dam" 但不会建议 "damn"
# 用户主动输入 "damn" → 标记为正确(不报错)
5.7 正则匹配条件
5.7.1 基础正则语法
Hunspell 的匹配条件使用类似正则表达式的语法:
| 语法 | 含义 | 示例 |
|---|---|---|
. | 任意字符 | . 匹配任何词根 |
[abc] | 字符集 | [aeiou] 匹配元音 |
[^abc] | 排除字符集 | [^aeiou] 匹配辅音 |
[a-z] | 范围 | [a-z] 匹配小写字母 |
[A-Z] | 范围 | [A-Z] 匹配大写字母 |
5.7.2 UTF-8 模式下的特殊语法
# UTF-8 编码时,特殊字符需要使用 Unicode 转义
SET UTF-8
# 匹配德语元音变音
SFX P Y 1
SFX P 0 \u00FCber [aou] # u → ü 的 umlaut
# 匹配法语重音字符
SFX E Y 1
SFX E er \u00E9 e # e → é
5.7.3 匹配条件示例
# 匹配以 "s", "x", "z", "sh", "ch", "tch" 结尾的词
SFX S Y 2
SFX S 0 es [sxz] # box → boxes, quiz → quizzes
SFX S 0 es .*[hs] # church → churches, bush → bushes
# 匹配辅音+y 结尾
SFX S Y 1
SFX S y ies [^aeiou]y # city → cities(y 前是辅音)
# 匹配以 "o" 结尾的词(部分加 es)
SFX S Y 2
SFX S 0 es [o] # potato → potatoes, hero → heroes
SFX S 0 s [^o] # 其他:piano → pianos
5.8 规则调试与测试
5.8.1 测试单个规则
# 创建最小测试词典
cat > /tmp/test.aff << 'EOF'
SET UTF-8
FLAG long
SFX D Y 4
SFX D 0 d e
SFX D 0 ed [^e]
SFX D y ied [^aeiou]y
SFX D 0 ed [aeiou]y
EOF
cat > /tmp/test.dic << 'EOF'
5
love/D
walk/D
try/D
play/D
run/D
EOF
# 测试展开
echo "loved" | hunspell -d /tmp/test -l # 无输出 = 正确
echo "walked" | hunspell -d /tmp/test -l # 无输出 = 正确
echo "tried" | hunspell -d /tmp/test -l # 无输出 = 正确
echo "played" | hunspell -d /tmp/test -l # 无输出 = 正确
# 测试形态学
echo "loved" | hunspell -d /tmp/test -m
# loved st:love ts:past_tense
5.8.2 调试工具脚本
#!/bin/bash
# debug_affix.sh - 调试词缀规则
# 用法: ./debug_affix.sh <dic_base> <test_word>
DICT_BASE="$1"
TEST_WORD="$2"
echo "=== 词缀规则调试 ==="
echo "词典: $DICT_BASE"
echo "测试词: $TEST_WORD"
echo ""
# 检查是否在词典中
echo "[直接检查]"
echo "$TEST_WORD" | hunspell -d "$DICT_BASE" -l
if [ $? -eq 0 ] && [ -z "$(echo "$TEST_WORD" | hunspell -d "$DICT_BASE" -l)" ]; then
echo " ✓ 词典中存在"
else
echo " ✗ 词典中不存在"
fi
echo ""
echo "[形态分析]"
echo "$TEST_WORD" | hunspell -d "$DICT_BASE" -m
echo ""
echo "[词干提取]"
echo "$TEST_WORD" | hunspell -d "$DICT_BASE" -s
echo ""
echo "[建议]"
echo "$TEST_WORD" | hunspell -a -d "$DICT_BASE" | grep "^&"
5.8.3 规则冲突检测
# 检查是否有重复/冲突的规则
grep -P '^SFX\s+(\S+)' test.aff | awk '{print $2}' | sort | uniq -c | sort -rn | head
# 如果同一标志出现超过一次,可能存在冲突
# 检查规则是否覆盖所有预期词形
cat > /tmp/test_words.txt << 'EOF'
walks
walked
walking
walks
tries
tried
trying
loves
loved
loving
EOF
echo "=== 规则覆盖测试 ==="
while read word; do
result=$(echo "$word" | hunspell -d /tmp/test -l)
if [ -z "$result" ]; then
echo " ✓ $word"
else
echo " ✗ $word (未识别)"
fi
done < /tmp/test_words.txt
5.9 常见模式速查表
5.9.1 英语常用模式
| 后缀 | 规则 | 适用词根 | 示例 |
|---|---|---|---|
| -s/-es | SFX S | 名词复数 | cats, boxes, cities |
| -ed | SFX D | 动词过去式 | walked, loved, tried |
| -ing | SFX G | 动词现在分词 | walking, making, running |
| -er | SFX R | 比较级 | taller, happier |
| -est | SFX T | 最高级 | tallest, happiest |
| -ly | SFX L | 副词 | slowly, happily |
| -ness | SFX N | 名词化 | darkness, happiness |
| -ment | SFX M | 名词化 | development |
| -tion/-sion | SFX O | 名词化 | education, decision |
| -able/-ible | SFX B | 形容词 | workable, accessible |
| -ful | SFX F | 形容词 | beautiful, hopeful |
| -less | SFX X | 形容词 | hopeless, careless |
| -ous | SFX U | 形容词 | dangerous, nervous |
| -ing (名) | SFX I | 名词化 | building, reading |
5.9.2 前缀规则速查
| 前缀 | 标志 | 含义 | 示例 |
|---|---|---|---|
| un- | PFX U | 否定/撤销 | unhappy, undo |
| re- | PFX R | 重复 | rewrite, redo |
| dis- | PFX D | 否定 | disagree, dislike |
| mis- | PFX M | 错误 | misspell, misfire |
| over- | PFX V | 过度 | overcook, overuse |
| under- | PFX W | 不足 | undercook, underuse |
| pre- | PFX P | 之前 | preview, precondition |
| post- | PFX Q | 之后 | postwar, postpone |
| in-/im- | PFX I | 否定 | impossible, invisible |
| non- | PFX N | 非 | nonsense, nonprofit |
| anti- | PFX A | 反对 | antibiotic, antisocial |
| inter- | PFX T | 之间 | international |
| trans- | PFX X | 跨越 | transport, transform |
5.10 本章小结
| 概念 | 说明 |
|---|---|
| SFX | 后缀规则,作用于词根末尾 |
| PFX | 前缀规则,作用于词根开头 |
| 可组合 (Y/N) | Y 允许与其他后缀叠加 |
| 条件替换 | 删除词根末尾字符再添加 |
| 匹配条件 | 正则表达式,匹配词根末尾 |
| CIRCUMFIX | 前后缀必须同时出现 |
| NEEDAFFIX | 词根必须有词缀才能独立成词 |
| KEEPCASE | 强制保持特定大小写 |
| NOSUGGEST | 不在建议列表中出现 |