清洗管线重构为严格三层架构:
- tango_analyser.py(底层:单词分析)
- task_processor.py(中层:文件 I/O、桶管理)
- workflow.py(顶层:状态机、任务推进)
- 移除旧的 batch_processor.py
新增配置文件系统:
- config.py:TOML 配置,相对路径相对配置文件目录解析
- 查找优先级 --config > cwd > 项目根 > ~ > 默认值
- count=None 语义为处理到文件末尾
项目目录整理:
- 根脚本归档到 scripts/analysis 与 scripts/legacy
- 文档归档到 docs/{design,history,analysis}
- 临时报告移到 reports/(已 gitignore)
文档质量:
- 新增 .markdownlint.json 与 scripts/mdlint.cmd
- 修复全部 14 个 md 文件的 markdownlint 警告
测试:74 passed(8 cleaner + 31 workflow + 6 tango + 15 validator + 14 config)
12 KiB
12 KiB
Japanese Cleaner - 日语词表清洗工具
工程化的日语词表清洗流水线,自动化处理汉字-假名-拼音对齐。采用三层架构 + 任务化: 清晰的职责分层,每次清洗是一个跨会话、有状态、可并存的任务。
完整协作规范见
WORKFLOW.md。
架构设计
三层职责分离(自底向上):
| 层 | 模块 | 职责 | 对外接口 |
|---|---|---|---|
| 底层 · 单词级 | tango_analyser.py |
接收单个词条 (kanji, kana),输出 AnalysisResult(格式化行 + 状态分类)。封装 Classifier / Aligner / PinyinMaker,不碰文件、不知道"桶"。 |
TangoAnalyser.analyze() |
| 中间层 · 文件级 | task_processor.py |
持有 Task(因此知道所有文件路径),读源文件/review 文件 → 调底层 → 按状态分流写入桶文件;合并单批到权威库。只搬文件,不改任务状态。 | TaskProcessor.process_source() / .process_review() / .merge_final() |
| 工作流层 · 编排级 | workflow.py |
给定任务判断走到哪一步、下一步做什么,唯一接口 run() 推进任务;更新任务状态机 created → reviewing/ready → merged。 |
CleanerWorkflow.run() |
任务模型:
Task/TaskConfig/TaskState— 配置 + 状态机 + 独立目录TaskManager— 任务 CRUD,多任务并存
设计原则:
- 底层不知道文件/桶,只返回状态枚举(
AnalysisStatus) - 中间层持有路径、负责 I/O,但不决策"该做什么"
- 工作流层只做决策/编排,委托中间层执行文件操作
功能特性
- 三层架构:职责清晰,底层可独立测试,工作流可编排复杂逻辑
- 配置文件管理:TOML 格式,支持相对/绝对路径,多项目友好
- 任务化:每次清洗是独立任务(独立配置 + 独立临时文件 + 状态机),多任务可并存
- 两层分离:单批临时工作区 / 最终只增去重的权威库
- 数据源只读:处理时不修改原文件,支持任意切片(起始行 + 条数)
- 严格校验:输入输出条数自动校验
- 半自动协作:自动分流 + 人工 review + 改代码重跑
- 工作流推进:
run()一键推进,能自动处理就处理,需人工就提示 - 单元测试:73 个测试全覆盖(底层单词分析 / 文件处理 / 工作流编排 / 配置管理)
数据目录布局
data/
├── db/ # 权威成品库(只增不删 + 去重)
│ ├── vocabulary.txt # 所有批次累积的成品
│ └── skipped.txt # 所有批次累积的跳过项
├── sources/ # 原始数据源(只读)
│ └── xinbiaori_1.txt
└── backup/ # 备份
└── *.backup.txt
tasks/
└── {task_id}/ # 每个任务独立目录
├── task.json # 任务配置 + 状态
├── auto_done.txt # 单批结果
├── skip.txt
└── review_*.txt # 待确认桶(pinyin/split/verb/special)
入口方式
本项目的清洗流程需要人工介入(review 循环:改字典/规则 → 重跑),因此
推荐用测试驱动协作(见 tests/test_cleaner_workflow.py),
命令行只是便捷入口。
三种等价的命令行入口:
jclean <cmd> ... # 安装后的 console 命令(推荐)
python -m pl_japanese.cleaner.cli <cmd> ... # 模块调用(无需 console 入口)
python scripts/clean.py <cmd> ... # 兼容薄壳(未安装包时)
安装(开发模式,注册 jclean 命令):
pip install -e .
配置文件
支持 TOML 格式配置文件,路径可以是相对路径(相对配置文件所在目录)或绝对路径。
生成示例配置
jclean init-config
# 生成 jclean.toml,编辑后自动生效
配置文件示例
# jclean.toml
[paths]
# 任务根目录(存放所有任务的独立目录)
tasks_root = "tasks"
# 权威库(所有任务最终合并的目标)
vocabulary = "data/db/vocabulary.txt"
skipped = "data/db/skipped.txt"
# 数据源目录(可选)
sources_dir = "data/sources"
[defaults]
# 创建任务时的默认值
start_line = 1
count = 300
# 是否在合并前自动备份权威库
backup_before_merge = true
backup_dir = "data/backup"
[logging]
# 日志级别:DEBUG / INFO / WARNING / ERROR
level = "INFO"
配置文件查找顺序
- 命令行参数
--config /path/to/config.toml(最高优先级) - 当前目录
./jclean.toml或./.jclean.toml - 向上查找项目根(遇到
.git停止) - 用户主目录
~/.jclean.toml - 硬编码默认值
路径解析规则
- 绝对路径:直接使用
- 相对路径:相对配置文件所在目录
示例(配置文件在 /home/user/project/jclean.toml):
[paths]
tasks_root = "tasks" # → /home/user/project/tasks
vocabulary = "/data/global/vocab.txt" # → /data/global/vocab.txt(绝对)
sources_dir = "../shared/sources" # → /home/user/shared/sources
查看当前配置
jclean show-config
# 显示当前生效的配置(包含解析后的绝对路径)
优先级
命令行参数 > 配置文件 > 默认值
# 配置文件中 tasks_root = "tasks"
# 命令行参数覆盖配置文件
jclean --tasks-root /custom/tasks create batch1 --source ...
快速开始
CLI 方式(极简命令)
# 1. 创建任务(数据源只读,可指定处理范围)
jclean create batch1 --source data/sources/xinbiaori_1.txt --start 1 --count 300
# 2. 工作流推进(自动判断下一步,能做就做,需人工就提示)
jclean run batch1
# 3. 如果有 review 待确认:人工修正字典/规则后,重跑指定桶
jclean run batch1 --bucket pinyin
# 4. 继续推进(review 清零后自动合并)
jclean run batch1
# 5. 完成!
# 其他命令
jclean list # 列举所有任务
jclean status batch1 # 查看任务状态
jclean clear batch1 # 清空单批文件
代码调用(三层 API)
工作流层(推荐,高层抽象)
from pl_japanese.cleaner import TaskManager, CleanerWorkflow
tm = TaskManager()
task = tm.create_task(
task_id='batch1',
source='data/sources/xinbiaori_1.txt',
start_line=1,
count=300,
)
wf = CleanerWorkflow(task)
# 自动推进
result = wf.run()
print(result['action']) # 'processed' / 'need_human' / 'completed'
print(result['status']) # 'reviewing' / 'ready' / 'merged'
print(result['message']) # 处理结果说明
# 如果需要人工介入
if result['action'] == 'need_human':
print(result['review']) # 待确认的 review 桶摘要
# 人工修正字典/规则后,重跑指定桶
wf.processor.process_review('pinyin')
result = wf.run() # 继续推进
# 循环推进直到完成
while result['action'] != 'completed':
result = wf.run()
if result['action'] == 'need_human':
break
中间层(文件操作,精细控制)
from pl_japanese.cleaner import TaskManager, TaskProcessor
tm = TaskManager()
task = tm.load_task('batch1')
proc = TaskProcessor(task)
# 处理源文件
result = proc.process_source()
print(result['buckets']) # 各桶条数
print(result['review_total']) # 需 review 的总数
# 重跑某个 review 桶
result = proc.process_review('pinyin')
# 合并(dry-run 校验)
result = proc.merge_final(dry_run=True)
# 查询当前状态
print(proc.snapshot_counts()) # 各单批桶当前条数
print(proc.review_total()) # review 总数
print(proc.final_counts()) # 权威库条数
底层(单词分析,测试/调试用)
from pl_japanese.cleaner import TangoAnalyser, AnalysisStatus
analyser = TangoAnalyser()
# 分析单个词条
result = analyser.analyze("日本人", "にほんじん")
print(result.status) # AnalysisStatus.SUCCESS
print(result.formatted_line) # "日|本|人:に|ほん|じん:ri|ben|ren"
print(result.is_success) # True
print(result.needs_review) # False
# 需人工确认的词
result = analyser.analyze("女将", "おかみ")
print(result.status) # AnalysisStatus.SPLIT_FAILED
print(result.needs_review) # True
核心概念
状态分类(底层)
AnalysisStatus 枚举(底层单词分析结果):
| 状态 | 含义 | 是否需 review |
|---|---|---|
SUCCESS |
成功处理,格式化行可直接采用 | ❌ |
SKIP |
无汉字等,跳过不进成品库 | ❌ |
POLYPHONE |
含多音字/多解,拼音需人工确认 | ✅ |
SPLIT_FAILED |
假名无法与汉字对齐分割 | ✅ |
VERB_FORM |
动词/敬语,需确认形式 | ✅ |
SPECIAL_CASE |
含字母/片假名/格式异常 | ✅ |
状态机(工作流层)
任务状态流转:
created
↓ run() → process_source
reviewing (有 review_* 待确认)
↓ 人工改代码/字典 + process_review → review 清零
↓ run() → 检测清零
ready (可合并)
↓ run() → merge_final
merged (完成)
↓ run()
completed (终态)
单批桶(中间层文件分类)
| 桶名 | 对应状态 | 含义 |
|---|---|---|
auto_done.txt |
SUCCESS | 自动处理成功,待合并进 vocabulary.txt |
skip.txt |
SKIP | 跳过(无汉字等),待合并进 skipped.txt |
review_pinyin.txt |
POLYPHONE | 多音字待确认 |
review_split.txt |
SPLIT_FAILED | 分割失败待人工 |
review_verb.txt |
VERB_FORM | 动词形式待确认 |
review_special.txt |
SPECIAL_CASE | 特殊情况待判断 |
工作流 run() 返回
CleanerWorkflow.run() 返回 dict,关键字段:
| 字段 | 含义 |
|---|---|
action |
processed(推进了)/ need_human(需人工)/ completed(完成)/ empty(无内容) |
status |
推进后的任务状态(created / reviewing / ready / merged) |
message |
人类可读说明 |
review |
(need_human 时)待确认的 review 桶摘要 {bucket: {count, sample}} |
process |
(processed 时)处理结果统计 |
merge |
(processed 且合并时)合并结果统计 |
分类规则
核心规则详见 src/pl_japanese/cleaner/classifier.py。
汉字分词(自动):
- 每个汉字单独一段
~(通配符)单独一段,匹配任意长假名- 其余非汉字连续合并成一段
拼音生成:
- 汉字:查
WORD_OVERRIDE全词覆写 → 查KANA_HINT假名提示 → pypinyin - 多音字检测:pypinyin 返回多个候选 → 标记
POLYPHONE
动词处理:
- 假名以
う段结尾 / 含~标记 / 假名末有 "ます/ません/ました" → 判定为动词 - 去掉送り仮名(活用后缀)后处理
跳过规则:
- 无汉字 →
SKIP - 纯片假名/字母/符号 →
SKIP
字典路径(多音字覆写):
WORD_OVERRIDE:全词精确匹配(优先级最高)KANA_HINT:(kanji, kana_prefix)→ pinyin,用于区分假名提示多音字
测试
# 运行所有测试(60 个)
pytest tests/
# 只测工作流(31 个)
pytest tests/test_cleaner_workflow.py -v
# 只测底层分析(8 个)
pytest tests/test_cleaner.py -v
测试覆盖:
- 底层单词分析:状态分类、格式输出、needs_review 逻辑
- 中间层文件处理:条数铁律、追加模式、去重、格式校验、review 重跑
- 工作流编排:状态机推进、人工介入门禁、review 清零自动转 ready、合并授权
- 任务管理:CRUD、多任务隔离、持久化
版本历史
- v0.3.0 (2025-01):三层架构重构 + 配置文件管理
- 三层职责清晰分离(底层单词分析 / 中间层文件处理 / 工作流编排)
- 新增
CleanerWorkflow.run()工作流推进 - CLI 精简(5 个命令,
run统一推进) - TOML 配置文件支持(相对/绝对路径)
- 73 个测试全覆盖
- v0.2.0 (2025-01):任务化架构,多任务并存,状态机管理
- v0.1.0 (2024):初版单批流水线
许可
MIT