Hunspell 拼写检查完全教程 / 第 10 章:最佳实践
第 10 章:最佳实践
10.1 词典维护最佳实践
10.1.1 词典版本控制
# 项目词典应纳入版本控制
# .gitignore 中不要忽略词典文件
# 词典目录结构推荐
.hunspell/
├── project.dic # 项目主词典
├── project.aff # 项目词缀文件(可选)
├── CHANGELOG.md # 词典变更记录
├── scripts/
│ ├── validate.sh # 验证脚本
│ ├── merge.sh # 合并脚本
│ └── extract.sh # 术语提取脚本
└── categories/
├── tech.dic # 技术术语
├── company.dic # 公司/产品名
└── abbreviations.dic # 缩略语
10.1.2 词典命名规范
| 文件 | 命名规则 | 示例 |
|---|
| 主词典 | <项目名>.dic | myproject.dic |
| 分类词典 | <类别>.dic | tech.dic, people.dic |
| 个人词典 | ~/.hunspell_personal | 系统级 |
| 临时词典 | .tmp.dic | 不提交到 Git |
10.1.3 词典审核流程
#!/bin/bash
# review_dict.sh — 词典审核脚本
# 检查词典中的潜在问题
DICT_FILE="$1"
echo "=== 词典审核: $DICT_FILE ==="
# 1. 检查是否有过短的词(可能是误添加)
echo "[过短词检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
awk '{if (length($0) <= 2) print " 警告: " $0}' | head -10
# 2. 检查是否有大写开头的词(可能是专有名词误加)
echo "[大写开头词检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
grep "^[A-Z]" | head -10 | sed 's/^/ /'
# 3. 检查是否有包含特殊字符的词
echo "[特殊字符检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
grep -P "[^a-zA-Z0-9/-]" | head -10 | sed 's/^/ /'
# 4. 检查 affix 标志是否有效
echo "[affix 标志检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
grep "/[^A-Z0-9]" | head -5 | sed 's/^/ 可能无效的标志: /'
echo ""
echo "=== 审核完成 ==="
10.1.4 定期词典清理
#!/bin/bash
# clean_dict.sh — 清理词典中的冗余/错误条目
# 用法: ./clean_dict.sh <词典文件>
DICT_FILE="$1"
BACKUP="${DICT_FILE}.bak.$(date +%Y%m%d)"
# 备份
cp "$DICT_FILE" "$BACKUP"
echo "已备份: $BACKUP"
# 清理步骤
{
# 保留第一行(大小声明,如果有)
head -1 "$DICT_FILE"
# 清理其余行
tail -n +2 "$DICT_FILE" | \
grep -v "^$" | # 删除空行
grep -v "^#" | # 删除注释
sed 's/[[:space:]]*$//' | # 删除行尾空格
sort -u # 去重排序
} > "${DICT_FILE}.clean"
# 统计
ORIGINAL=$(grep -cv "^#\|^$" "$DICT_FILE")
CLEAN=$(grep -cv "^#\|^$" "${DICT_FILE}.clean")
REMOVED=$((ORIGINAL - CLEAN))
echo "清理前: $ORIGINAL 条"
echo "清理后: $CLEAN 条"
echo "移除: $REMOVED 条"
# 替换
mv "${DICT_FILE}.clean" "$DICT_FILE"
echo "已更新: $DICT_FILE"
10.1.5 词典文档化
<!-- .hunspell/CHANGELOG.md -->
# 词典变更记录
## 2024-01-15
- 添加 42 个 Kubernetes 相关术语
- 添加 15 个内部产品名称
- 移除 3 个废弃产品名
## 2024-01-01
- 初始化项目词典
- 从项目文档中提取 120 个术语
## 统计
- 当前词条数: 247
- 技术术语: 189
- 产品名称: 35
- 缩略语: 23
10.2 性能优化
10.2.1 词典大小声明优化
# 第一行声明词典大小,影响哈希表初始化
# 应该尽量接近实际词条数
# 查看实际词条数
grep -cv "^#\|^$" project.dic
# 更新声明
sed -i "1s/.*/$(grep -cv '^#\|^$' project.dic)/" project.dic
10.2.2 大文件检查优化
# 方案 1: 并行检查
find . -name "*.md" -print0 | \
xargs -0 -P $(nproc) -I{} \
sh -c 'hunspell -l -d en_US -p .hunspell/project.dic "{}"'
# 方案 2: 先提取文本,再批量检查
cat *.md | \
grep -v "^#\|^\`\`\`\|^$" | \
hunspell -l -d en_US -p .hunspell/project.dic | \
sort -u
# 方案 3: 缓存检查结果
CACHE_FILE="/tmp/spellcheck_cache.txt"
find . -name "*.md" -newer "$CACHE_FILE" -exec \
hunspell -l -d en_US -p .hunspell/project.dic {} + \
>> "$CACHE_FILE"
sort -u "$CACHE_FILE"
10.2.3 内存使用优化
# 对于大型词典,限制并发进程数
# 避免同时加载多个词典实例
# 使用 -B 参数限制缓冲区大小(如果支持)
# hunspell -B 4096 ...
10.2.4 选择性检查
#!/bin/bash
# smart_check.sh — 智能拼写检查
# 只检查自上次构建以来变更的文件
CHECKED_FILES="/tmp/checked_files.txt"
touch "$CHECKED_FILES"
find . -name "*.md" -newer "$CHECKED_FILES" -print0 | \
while IFS= read -r -d '' file; do
echo "检查: $file"
hunspell -l -d en_US -p .hunspell/project.dic "$file" | sort -u
# 标记为已检查
touch -r "$file" "$CHECKED_FILES"
done
10.2.5 批处理模式优化
# 合并多个文件一次检查(减少进程启动开销)
find . -name "*.md" -exec cat {} + | \
hunspell -l -d en_US -p .hunspell/project.dic | \
sort -u > /tmp/all_errors.txt
# 然后用 grep 定位每个文件中的错误
while read error; do
grep -rl "$error" *.md
done < /tmp/all_errors.txt
10.3 误报处理
10.3.1 常见误报类型
| 类型 | 示例 | 处理方法 |
|---|
| 专有名词 | Hunspell, GitHub | 添加到项目词典 |
| 技术术语 | API, JSON, HTTP | 添加到项目词典 |
| 缩略语 | URL, HTML, CSS | 添加到项目词典 |
| 代码片段 | function(), const x = 1 | 正则排除 |
| URL 链接 | https://example.com | 正则排除 |
| 版本号 | v1.2.3, 2024.01 | 正则排除 |
| 路径 | /usr/local/bin | 正则排除 |
| 邮箱 | user@example.com | 正则排除 |
10.3.2 正则排除策略
# 使用 grep 预处理,排除特定模式
cat document.md | \
grep -v '```' | # 排除代码块
grep -v '^\s*#' | # 排除标题(可选)
sed 's/`[^`]*`//g' | # 排除行内代码
sed 's/https\?:[^ )]*//g' | # 排除 URL
sed 's/[a-zA-Z0-9._%+-]\+@[a-zA-Z0-9.-]\+\.[a-zA-Z]\{2,\}//g' | # 排除邮箱
sed 's/v\?[0-9]\+\.[0-9]\+\.[0-9]\+//g' | # 排除版本号
hunspell -l -d en_US -p .hunspell/project.dic
10.3.3 Python 预处理
#!/usr/bin/env python3
"""拼写检查预处理 — 排除特定模式"""
import re
import subprocess
def preprocess_text(text: str) -> str:
"""预处理文本,排除不需检查的内容"""
# 排除代码块
text = re.sub(r'```[\s\S]*?```', '', text)
# 排除行内代码
text = re.sub(r'`[^`]+`', '', text)
# 排除 URL
text = re.sub(r'https?://\S+', '', text)
# 排除邮箱
text = re.sub(r'\S+@\S+\.\S+', '', text)
# 排除版本号
text = re.sub(r'v?\d+\.\d+\.\d+', '', text)
# 排除路径
text = re.sub(r'/\S+', '', text)
# 排除 Markdown 链接文本中的 URL
text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
# 排除 HTML 标签
text = re.sub(r'<[^>]+>', '', text)
return text
def spellcheck_with_preprocessing(file_path: str, dictionary: str = "en_US",
personal_dict: str = None) -> list[str]:
"""带预处理的拼写检查"""
with open(file_path, 'r', encoding='utf-8') as f:
text = f.read()
processed = preprocess_text(text)
cmd = ["hunspell", "-l", "-d", dictionary]
if personal_dict:
cmd.extend(["-p", personal_dict])
result = subprocess.run(
cmd, input=processed,
capture_output=True, text=True
)
return [w for w in result.stdout.strip().split("\n") if w]
# 使用
errors = spellcheck_with_preprocessing(
"document.md",
personal_dict=".hunspell/project.dic"
)
print(f"发现 {len(errors)} 个错误")
10.3.4 项目词典增量更新
#!/bin/bash
# update_dict.sh — 增量更新项目词典
# 用法: ./update_dict.sh <新词文件> [词典文件]
NEW_WORDS="$1"
DICT_FILE="${2:-.hunspell/project.dic}"
echo "=== 增量更新词典 ==="
echo "新词文件: $NEW_WORDS"
echo "词典文件: $DICT_FILE"
# 备份
cp "$DICT_FILE" "${DICT_FILE}.bak"
# 添加新词(跳过已存在的)
ADDED=0
while read word; do
# 跳过空行和注释
[[ -z "$word" || "$word" =~ ^# ]] && continue
# 检查是否已存在
if ! grep -q "^${word}$" "$DICT_FILE"; then
echo "$word" >> "$DICT_FILE"
ADDED=$((ADDED + 1))
fi
done < "$NEW_WORDS"
# 更新大小声明(如果有)
TOTAL=$(grep -cv "^#\|^$" "$DICT_FILE")
sed -i "1s/.*/$TOTAL/" "$DICT_FILE"
echo "新增: $ADDED 个词条"
echo "总计: $TOTAL 个词条"
10.3.5 误报白名单模式
// cspell.json 白名单配置
{
"version": "0.2",
// 全局忽略模式
"ignoreRegExpList": [
"/`[^`]+`/g", // 行内代码
"/```[\\s\\S]*?```/g", // 代码块
"/https?:\\/\\/[^\\s]+/g", // URL
"/[a-f0-9]{7,40}/gi", // Git commit hash
"/v\\d+\\.\\d+\\.\\d+/g", // 版本号
"/\\b[A-Z][a-z]*[A-Z][a-z]*\\b/g" // camelCase(可选)
],
// 文件级忽略
"ignorePaths": [
"node_modules",
"package-lock.json",
"*.min.js",
"*.map",
".git"
],
// 单词级忽略
"words": [
"Hunspell",
"Docker",
"Kubernetes"
],
// 标记为错误的词(即使在词典中)
"flagWords": [
"teh",
"adn",
"hte"
]
}
10.4 多语言策略
10.4.1 多语言文档处理
#!/usr/bin/env python3
"""多语言文档拼写检查"""
import re
import subprocess
from langdetect import detect
def detect_language(text_segment: str) -> str:
"""检测文本段的语言"""
try:
lang = detect(text_segment)
# 映射到 Hunspell 词典代码
lang_map = {
'en': 'en_US', 'fr': 'fr', 'de': 'de_DE',
'es': 'es_ES', 'pt': 'pt_BR', 'it': 'it',
'ru': 'ru_RU', 'zh-cn': 'zh_CN', 'ja': 'ja',
}
return lang_map.get(lang, 'en_US')
except:
return 'en_US'
def multilingual_spellcheck(file_path: str) -> dict[str, list[str]]:
"""多语言拼写检查"""
with open(file_path, 'r', encoding='utf-8') as f:
text = f.read()
# 按段落分割
paragraphs = re.split(r'\n\s*\n', text)
all_errors = {}
for i, para in enumerate(paragraphs):
if len(para.strip()) < 10:
continue
# 检测语言
lang = detect_language(para)
# 检查拼写
cmd = ["hunspell", "-l", "-d", lang]
result = subprocess.run(cmd, input=para, capture_output=True, text=True)
errors = [w for w in result.stdout.strip().split("\n") if w]
if errors:
if lang not in all_errors:
all_errors[lang] = []
all_errors[lang].extend(errors)
# 去重
for lang in all_errors:
all_errors[lang] = sorted(set(all_errors[lang]))
return all_errors
# 使用
errors = multilingual_spellcheck("multilingual_doc.md")
for lang, words in errors.items():
print(f"\n{lang} 错误 ({len(words)} 个):")
for word in words[:10]:
print(f" → {word}")
10.4.2 语言切换标记
# 使用 HTML lang 属性标记语言
# <span lang="en">English text</span>
# <span lang="zh">中文文本</span>
# 使用 Markdown 扩展
# <!-- lang:en -->
# This is English text.
# <!-- lang:fr -->
# Ceci est du texte français.
10.4.3 词典合并策略
#!/bin/bash
# merge_multilingual.sh — 合并多语言词典
# 用于混合语言文档
LANG1_DICT="$1" # 如 en_US
LANG2_DICT="$2" # 如 fr
OUTPUT_DICT="$3" # 输出词典
echo "合并: $LANG1_DICT + $LANG2_DICT → $OUTPUT_DICT"
# 提取两个词典的词根
{
# 去掉 affix 标志,只保留词根
grep -v "^#\|^$" "$LANG1_DICT.dic" | sed 's/\/.*//' | sort -u
grep -v "^#\|^$" "$LANG2_DICT.dic" | sed 's/\/.*//' | sort -u
} | sort -u > "$OUTPUT_DICT.dic"
# 合并 affix 文件(使用第一个语言的 affix)
cp "$LANG1_DICT.aff" "$OUTPUT_DICT.aff"
# 更新大小声明
TOTAL=$(wc -l < "$OUTPUT_DICT.dic")
sed -i "1i $TOTAL" "$OUTPUT_DICT.dic"
echo "合并完成: $TOTAL 个词条"
10.4.4 多语言配置最佳实践
| 场景 | 策略 | 实现方式 |
|---|
| 单语言文档 | 使用对应词典 | -d en_US |
| 双语混合 | 合并词典 | 词典合并脚本 |
| 多语种文档 | 语言检测 + 切换 | langdetect + hunspell |
| 团队协作 | 统一项目词典 | .hunspell/project.dic |
| CI/CD | 按文件类型检查 | 文件模式匹配 |
10.5 生产环境部署
10.5.1 微服务架构
# docker-compose.yml — 拼写检查微服务
version: '3.8'
services:
spellcheck:
build: .
ports:
- "8080:8080"
volumes:
- ./dictionaries:/opt/dictionaries:ro
environment:
- HUNSPELL_DICT_PATH=/opt/dictionaries
- DEFAULT_LANG=en_US
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
replicas: 2
resources:
limits:
memory: 256M
cpus: '0.5'
10.5.2 缓存策略
#!/usr/bin/env python3
"""带缓存的拼写检查服务"""
from functools import lru_cache
import hashlib
class CachedSpellChecker:
"""缓存拼写检查结果,避免重复检查相同单词"""
def __init__(self, dictionary: str = "en_US"):
self.dictionary = dictionary
self._cache = {}
self._cache_hits = 0
self._cache_misses = 0
def _check_uncached(self, word: str) -> bool:
"""实际调用 Hunspell 检查"""
import subprocess
result = subprocess.run(
["hunspell", "-d", self.dictionary, "-l"],
input=word, capture_output=True, text=True
)
return word not in result.stdout
def check(self, word: str) -> bool:
"""带缓存的检查"""
# 使用小写作为缓存键(如果不需要大小写敏感)
key = word.lower()
if key in self._cache:
self._cache_hits += 1
return self._cache[key]
self._cache_misses += 1
result = self._check_uncached(word)
self._cache[key] = result
return result
@property
def cache_stats(self) -> dict:
total = self._cache_hits + self._cache_misses
return {
"size": len(self._cache),
"hits": self._cache_hits,
"misses": self._cache_misses,
"hit_rate": self._cache_hits / total if total > 0 else 0
}
# 使用
checker = CachedSpellChecker("en_US")
# 批量检查
words = ["hello", "world", "helo", "hello", "world"] # hello/world 会命中缓存
for word in words:
checker.check(word)
print(checker.cache_stats)
# {'size': 3, 'hits': 2, 'misses': 3, 'hit_rate': 0.4}
10.5.3 监控与告警
#!/usr/bin/env python3
"""拼写检查服务监控"""
import time
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class SpellCheckMetrics:
"""拼写检查指标"""
total_checks: int = 0
total_errors: int = 0
total_time_ms: float = 0
cache_hits: int = 0
cache_misses: int = 0
errors_by_language: dict = field(default_factory=dict)
@property
def error_rate(self) -> float:
return self.total_errors / self.total_checks if self.total_checks > 0 else 0
@property
def avg_check_time_ms(self) -> float:
return self.total_time_ms / self.total_checks if self.total_checks > 0 else 0
@property
def cache_hit_rate(self) -> float:
total = self.cache_hits + self.cache_misses
return self.cache_hits / total if total > 0 else 0
class MonitoredSpellChecker:
"""带监控的拼写检查器"""
def __init__(self, checker):
self.checker = checker
self.metrics = SpellCheckMetrics()
self._start_time = None
def check(self, word: str, language: str = "en") -> bool:
start = time.time()
result = self.checker.check(word)
elapsed = (time.time() - start) * 1000
self.metrics.total_checks += 1
self.metrics.total_time_ms += elapsed
if not result:
self.metrics.total_errors += 1
self.metrics.errors_by_language[language] = \
self.metrics.errors_by_language.get(language, 0) + 1
return result
def get_metrics(self) -> dict:
return {
"total_checks": self.metrics.total_checks,
"total_errors": self.metrics.total_errors,
"error_rate": f"{self.metrics.error_rate:.2%}",
"avg_time_ms": f"{self.metrics.avg_check_time_ms:.2f}",
"cache_hit_rate": f"{self.metrics.cache_hit_rate:.2%}",
"errors_by_language": self.metrics.errors_by_language
}
# Prometheus 指标输出示例
def prometheus_metrics(metrics: dict) -> str:
"""生成 Prometheus 格式指标"""
lines = [
f'# HELP spellcheck_total Total spell checks performed',
f'# TYPE spellcheck_total counter',
f'spellcheck_total {metrics["total_checks"]}',
f'# HELP spellcheck_errors_total Total spelling errors found',
f'# TYPE spellcheck_errors_total counter',
f'spellcheck_errors_total {metrics["total_errors"]}',
f'# HELP spellcheck_duration_seconds Average check duration',
f'# TYPE spellcheck_duration_seconds gauge',
f'spellcheck_duration_seconds {float(metrics["avg_time_ms"]) / 1000:.6f}',
]
return "\n".join(lines)
10.5.4 高可用部署
# Kubernetes 部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: spellcheck-service
spec:
replicas: 3
selector:
matchLabels:
app: spellcheck
template:
metadata:
labels:
app: spellcheck
spec:
containers:
- name: spellcheck
image: spellcheck:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: dictionaries
mountPath: /opt/dictionaries
readOnly: true
volumes:
- name: dictionaries
configMap:
name: spellcheck-dictionaries
---
apiVersion: v1
kind: Service
metadata:
name: spellcheck-service
spec:
selector:
app: spellcheck
ports:
- port: 80
targetPort: 8080
type: ClusterIP
10.6 团队协作
10.6.1 词典所有权
# .github/CODEOWNERS
# 词典文件变更需要特定团队审核
.hunspell/ @docs-team @tech-leads
.hunspell/project.dic @docs-team
10.6.2 词典更新流程
# 1. 创建分支
git checkout -b feat/add-project-terms
# 2. 添加新词到词典
echo "Kubernetes" >> .hunspell/project.dic
echo "Helm" >> .hunspell/project.dic
# 3. 验证词典
./.hunspell/scripts/validate.sh .hunspell/project.dic
# 4. 测试拼写检查
hunspell -d en_US -p .hunspell/project.dic -l docs/*.md
# 5. 提交 PR
git add .hunspell/project.dic
git commit -m "feat: add Kubernetes and Helm to project dictionary"
git push origin feat/add-project-terms
10.6.3 团队词典规范
<!-- .hunspell/CONTRIBUTING.md -->
# 项目词典贡献指南
## 添加新词
1. 确认该词确实需要添加(不是临时拼写)
2. 检查该词是否已存在
3. 将词添加到正确的分类词典
4. 如果需要 affix 标志,参考现有模式
## 命名规范
- 公司/产品名:保持官方大小写(如 `GitHub` 而非 `github`)
- 技术术语:使用最常见的拼写形式
- 缩略语:使用大写形式(如 `API` 而非 `api`)
## 不应添加的词
- 临时变量名
- 个人名字(除非频繁出现)
- 明显的拼写错误
10.7 安全考虑
10.7.1 词典文件安全
# 词典文件权限设置
chmod 644 .hunspell/project.dic # 所有者可写,其他人只读
chmod 644 .hunspell/project.aff
# 避免将敏感信息放入词典
# 不要添加:密码、API 密钥、内部代号
10.7.2 输入验证
def sanitize_input(text: str, max_length: int = 10000) -> str:
"""清理用户输入,防止注入攻击"""
# 限制输入长度
if len(text) > max_length:
text = text[:max_length]
# 移除控制字符
text = ''.join(c for c in text if c.isprintable() or c in '\n\r\t')
return text
10.7.3 资源限制
import signal
class TimeoutError(Exception):
pass
def timeout_handler(signum, frame):
raise TimeoutError("拼写检查超时")
def check_with_timeout(text: str, timeout: int = 30) -> list[str]:
"""带超时的拼写检查"""
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout)
try:
result = subprocess.run(
["hunspell", "-l", "-d", "en_US"],
input=text, capture_output=True, text=True,
timeout=timeout
)
return [w for w in result.stdout.strip().split("\n") if w]
except subprocess.TimeoutExpired:
return ["错误:检查超时"]
finally:
signal.alarm(0)
10.8 故障排查
10.8.1 常见问题及解决方案
| 问题 | 原因 | 解决方案 |
|---|
| 找不到词典 | 路径错误或未安装 | hunspell -D 查看搜索路径 |
| 编码错误 | 文件编码与词典不匹配 | 检查 SET UTF-8,用 file -i 验证 |
| 建议为空 | TRY 列表缺失 | 在 .aff 文件中添加 TRY |
| 性能差 | 词典大小声明不准 | 更新第一行数字 |
| 复合词误报 | 复合词规则不完善 | 添加 COMPOUNDRULE |
| 专有名词报错 | 未添加到词典 | 添加到项目词典 |
10.8.2 诊断脚本
#!/bin/bash
# diagnose.sh — Hunspell 诊断脚本
echo "=== Hunspell 环境诊断 ==="
echo "[版本信息]"
hunspell --version 2>&1 | head -3
echo ""
echo "[词典搜索路径]"
hunspell -D 2>&1 | head -20
echo ""
echo "[可用词典]"
hunspell -D 2>&1 | grep -v "^搜索" | grep -v "^$" | head -10
echo ""
echo "[系统词典目录]"
ls -la /usr/share/hunspell/ 2>/dev/null | head -10
echo ""
echo "[用户词典]"
ls -la ~/.hunspell* 2>/dev/null || echo " 未找到用户词典"
echo ""
echo "[环境变量]"
echo " DICPATH=${DICPATH:-未设置}"
echo " LANG=${LANG:-未设置}"
echo " LC_ALL=${LC_ALL:-未设置}"
echo ""
echo "[基本功能测试]"
echo "hello" | hunspell -d en_US -l 2>&1
if [ $? -eq 0 ]; then
echo " ✓ 基本检查正常"
else
echo " ✗ 基本检查失败"
fi
echo "helo" | hunspell -a -d en_US 2>&1 | grep "^&" | head -3
if [ $? -eq 0 ]; then
echo " ✓ 建议功能正常"
else
echo " ✗ 建议功能失败"
fi
echo ""
echo "=== 诊断完成 ==="
10.8.3 日志记录
import logging
import json
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('spellcheck.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger('spellcheck')
def log_spellcheck_result(file_path: str, errors: list[str], duration_ms: float):
"""记录拼写检查结果"""
logger.info(json.dumps({
"event": "spellcheck_complete",
"file": file_path,
"error_count": len(errors),
"duration_ms": round(duration_ms, 2),
"errors_sample": errors[:5],
"timestamp": datetime.now().isoformat()
}))
10.9 持续改进
10.9.1 词典质量指标
def calculate_dict_quality(errors: list[str], total_words: int) -> dict:
"""计算词典质量指标"""
error_count = len(errors)
unique_errors = len(set(errors))
return {
"total_words": total_words,
"total_errors": error_count,
"unique_errors": unique_errors,
"error_rate": f"{error_count / total_words:.2%}" if total_words > 0 else "0%",
"unique_error_rate": f"{unique_errors / total_words:.2%}" if total_words > 0 else "0%",
"quality_score": 1 - (unique_errors / total_words) if total_words > 0 else 1
}
10.9.2 定期审查计划
| 频率 | 活动 | 负责人 |
|---|
| 每周 | 检查 CI 拼写报告 | 文档团队 |
| 每月 | 清理冗余词典条目 | 技术负责人 |
| 每季度 | 审核词典分类 | 产品经理 |
| 每年 | 更新语言词典版本 | 运维团队 |
10.9.3 词典演进策略
阶段 1: 初始建立
- 从项目文档提取术语
- 建立基本分类结构
- 配置 CI/CD 集成
阶段 2: 持续优化
- 根据误报调整词典
- 细化分类
- 添加 affix 规则
阶段 3: 成熟运营
- 自动化词典更新
- 质量指标监控
- 团队协作流程
阶段 4: 智能化
- 基于历史数据的自动建议
- 上下文感知的误报过滤
- 多语言智能切换
10.10 总结与展望
10.10.1 Hunspell 的优势
| 优势 | 说明 |
|---|
| 广泛的语言支持 | 100+ 语言词典 |
| 高性能 | 基于词缀压缩,检查速度快 |
| 灵活的扩展性 | 自定义词典、affix 规则 |
| 成熟的生态系统 | 各平台/编辑器集成 |
| 开源许可 | LGPL/MPL/GPL,可自由集成 |
10.10.2 Hunspell 的局限
| 局限 | 替代方案 |
|---|
| 无上下文检查 | LanguageTool |
| 无语法检查 | Grammarly, LanguageTool |
| CJK 支持有限 | 分词工具 + 自定义词典 |
| 不规则词需要手动处理 | 补充词典 |
10.10.3 未来趋势
1. AI 辅助拼写检查
- 大语言模型结合传统规则引擎
- 上下文感知的自动纠正
2. 更好的多语言支持
- 统一的多语言检查框架
- 自动语言检测与切换
3. 实时协作
- 团队共享词典
- 云端同步
4. 领域特定优化
- 垂直领域词典(医疗、法律、金融)
- 行业术语自动提取
10.11 本章小结
| 实践领域 | 关键要点 |
|---|
| 词典维护 | 版本控制、分类管理、定期清理 |
| 性能优化 | 大小声明、并行检查、缓存 |
| 误报处理 | 正则排除、白名单、预处理 |
| 多语言 | 语言检测、词典合并、切换策略 |
| 生产部署 | 微服务、缓存、监控、高可用 |
| 团队协作 | 所有权、更新流程、贡献规范 |
| 故障排查 | 诊断脚本、日志记录、常见问题 |
扩展阅读
恭喜你完成了 Hunspell 完全教程! 🎉
本教程从 Hunspell 的历史背景讲起,覆盖了安装配置、基本使用、词典格式、词缀规则、自定义词典开发、编程接口、编辑器集成、形态学分析,直到生产环境的最佳实践。希望这些内容能帮助你在项目中高效地使用 Hunspell。
如有问题,欢迎参考 Hunspell 官方文档 或在项目中提出 Issue。