govdoc-md-cleaner v0.1.0: 政务文档 Markdown 清洗工具(规则引擎 + CLI + 测试)
- 8 条 YAML 声明规则:页码/页眉页脚/目录点线/图片/HTML表格/散落标签/行尾空白/空行 - 防误伤设计:protect 正则 + 内容形态豁免 + OCR burst 检测 - md-clean single/batch CLI,JSON 清洗报告 - 18 个单元测试 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""命令行入口。
|
||||
|
||||
用法:
|
||||
md-clean single <input.md> [-o output.md] [--diff] [--rules rules.yaml]
|
||||
md-clean batch <dir> [-o outdir] [--pattern "*.md"] [--report report.json]
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from cleaner.cleaner import MarkdownCleaner
|
||||
from cleaner.rules import load_rules
|
||||
|
||||
|
||||
def _build(path: Optional[Path]) -> MarkdownCleaner:
|
||||
return MarkdownCleaner(rules=load_rules(path))
|
||||
|
||||
|
||||
def cmd_single(args: argparse.Namespace) -> int:
|
||||
cleaner = _build(args.rules)
|
||||
src = Path(args.input)
|
||||
raw = src.read_text(encoding="utf-8")
|
||||
result = cleaner.clean_text(raw)
|
||||
if args.output:
|
||||
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(args.output).write_text(result.text, encoding="utf-8")
|
||||
print(f"已写入 {args.output}")
|
||||
if args.diff:
|
||||
print(MarkdownCleaner.diff(raw, result.text))
|
||||
print(json.dumps(result.stats, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_batch(args: argparse.Namespace) -> int:
|
||||
cleaner = _build(args.rules)
|
||||
src_dir = Path(args.directory)
|
||||
files = sorted(p for p in src_dir.rglob(args.pattern) if p.is_file())
|
||||
if not files:
|
||||
print(f"在 {src_dir} 下未找到匹配 {args.pattern} 的文件", file=sys.stderr)
|
||||
return 1
|
||||
out_dir = Path(args.output) if args.output else src_dir.parent / (src_dir.name + "_cleaned")
|
||||
report = []
|
||||
for f in files:
|
||||
rel = f.relative_to(src_dir)
|
||||
dst = out_dir / rel
|
||||
result = cleaner.clean_file(f, dst)
|
||||
report.append({"file": str(rel), "stats": result.stats})
|
||||
hits = result.total_hits
|
||||
print(f"[ok] {rel} 命中 {hits} 处")
|
||||
if args.report:
|
||||
Path(args.report).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(args.report).write_text(
|
||||
json.dumps({"files": report}, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
print(f"报告已写入 {args.report}")
|
||||
print(f"共清洗 {len(files)} 个文件 → {out_dir}")
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: Optional[List[str]] = None) -> int:
|
||||
parser = argparse.ArgumentParser(prog="md-clean", description="政务文档 Markdown 清洗工具")
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
p1 = sub.add_parser("single", help="清洗单个文件")
|
||||
p1.add_argument("input", help="输入 .md 文件")
|
||||
p1.add_argument("-o", "--output", help="输出路径(缺省打印统计不写文件)")
|
||||
p1.add_argument("--diff", action="store_true", help="打印 unified diff")
|
||||
p1.add_argument("--rules", type=Path, help="规则 YAML 路径")
|
||||
|
||||
p2 = sub.add_parser("batch", help="批量清洗目录(递归)")
|
||||
p2.add_argument("directory", help="输入目录")
|
||||
p2.add_argument("-o", "--output", help="输出目录(缺省 <dir>_cleaned)")
|
||||
p2.add_argument("--pattern", default="*.md", help="文件 glob(默认 *.md)")
|
||||
p2.add_argument("--report", help="清洗报告 JSON 输出路径")
|
||||
p2.add_argument("--rules", type=Path, help="规则 YAML 路径")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
if args.command == "single":
|
||||
return cmd_single(args)
|
||||
return cmd_batch(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user