初始归档:健康管理资料库(大医网/药膳/体质监测/中医理论等12个资料集,7712个文件)

This commit is contained in:
512song committed 2026-09-23 21:59:25 +08:00
commit 80ae3811cf
7712 files changed
+4628547

No files matched your search

@@ -0,0 +1,638 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
辅行诀五脏用药法要-药性探真 资料库 ETL 第 3 步(2026-09-16)
01_来源数据/辅行诀五脏用药法要.md(印刷书籍扫描本) -> 02_加工数据/
定位(用户 2026-09-16 指令):
本印刷本**优于**此前入库的古本原文(集成网系电子本)与维基文库参校本,
定为《辅行诀五脏用药法要》的**基础文本(底本)**;
参校本只在判断扫描讹字、脱字、脱分隔时作辅助证据,不作版本改写。
竖排本作「右X味」,横排/网络本作「上X味」——**以本书为准,一律不改**。
清理项:
A 版式残留:行首行尾空格、句中误入空格、LaTeX(PDF 公式化)残留、本地图片路径残留
B 断行/脱分隔:换页换行接合(20 处,行号锚定)、药味行脱分隔空格(4 处)、药味行与煎服法误并(2 处)
C 讹字修正:每条附核校依据(本书内证/参校本/同类文献)
D 字符规范化:半角标点 -> 全角
E 存疑登记:原文不改,逐条附疑正与参校本异文
F 版本差异/体例登记:本书特有内容与用字体例,不作校改
输出:
02_加工数据/辅行诀五脏用药法要_基础文本_清洗版.md
02_加工数据/印刷本清洗报告.md (核对数据按卷/篇列明 + 自检结果)
02_加工数据/印刷本人工核对清单.md
02_加工数据/印刷本结构_上下卷.json (卷 / 篇 / 方 三级 + 行号锚定)
幂等:可重复运行,输出覆盖生成。行号锚定全部带原文校验,源文件被替换时会直接报错。
"""
import os, re, json, hashlib
BASE = os.path.expanduser("~/Documents/ai_agent_scraper_study/data/辅行诀五脏用药法要-药性探真")
SRC = os.path.join(BASE, "01_来源数据/辅行诀五脏用药法要.md")
OUT_MD = os.path.join(BASE, "02_加工数据/辅行诀五脏用药法要_基础文本_清洗版.md")
OUT_REPORT = os.path.join(BASE, "02_加工数据/印刷本清洗报告.md")
OUT_CHECK = os.path.join(BASE, "02_加工数据/印刷本人工核对清单.md")
OUT_STRUCT = os.path.join(BASE, "02_加工数据/印刷本结构_上下卷.json")
REF_NAME = "维基文库本《辅行诀脏腑用药法要》(参校本,仅作查错补漏)"
OLD_NAME = "集成网系《辅行诀脏腑用药法要》古本原文(旧入库参考本)"
# ── B 类:换页/换行断行接合(行号锚定;值为 (接合符, 该行原文锚))─────────
JOINS = {
48: (" ", "竹叶各一两"),
52: ("", "土各一两"),
60: ("", "下血者,其变刺郄中血者。"),
94: ("", "癫仆。"),
112: ("", "烦躁,汗出,气噫,脉结者方。"),
124: ("", "者方。"),
154: ("", "得,或下利不止,足痿不收,肢冷脉微者方。"),
157: ("", "各一两"),
187: ("", "厥阴内血者。"),
224: (" ", "甘草炙,各一两"),
269: ("", "各一两"),
275: ("", "一两"),
388: ("", "捣碎"),
397: ("", "去核"),
406: ("", "去核"),
410: ("", "一服。"),
495: (" ", "半夏半升 干姜三两"),
557: ("", "活之。"),
580: ("", "硷各三两"),
}
# ── B 类:药味行与煎服法误并一行,须在指定标记前拆行(行号锚定)──────────
SPLITS = {32: "右六味", 108: "右六味", 252: "右方六味", 345: "右五味"}
# ── A 类:LaTeX(PDF 公式化)残留 -> 药味行(行号锚定,逐块核读)──────
# (起始行, 结束行, 还原文本, 说明, 结束行锚)
LATEX_BLOCKS = [
(381, 383, "石绿三两 芒硝六两 雄黄三两",
"小养生补肝汤散金石方,据药名与用量还原(药行「各三两/各六两」体例:君六两、余三两)",
"\\mathrm{雄黄三两}"),
(412, 414, "凝水石三两 丹砂六两 曾青三两",
"小凝息补肺汤散金石方,据药名与用量还原", "曾青三两"),
(418, 420, "人参三两 附子二大枚,炮 竹叶三两 薤白三两 苦杏七枚,去核擘",
"小固元补肾汤散草木方,双栏数组(\\begin{array})还原;「去核擘」为炮制注,归属待核(见存疑)",
"苦杏七枚"),
(424, 426, "赤石脂三两 阳起石六两 白垩土三两",
"小固元补肾汤散金石方,据药名与用量还原", "白垩土三两"),
]
# ── A 类:单行替换(图版引用等),行号锚定 ─────────────────────────────
LINE_REPLACE = {
458: ("2026-09-16-10-28-06-image.png",
"> 〔图版〕此处为《汤液经法图》(原书图版)。本电子本仅存本地路径引用"
"(`…marktext/images/…png`,图本体未随文提供),图与图注均缺,待补。"),
}
# ── A 类:句中误入空格(逐条定位,药味行分隔空格一律保留)──────────────
STRAY_SPACES = [
("诸 凡杂病", "诸凡杂病"),
("梁·华阳隐居陶弘景 撰", "梁·华阳隐居 陶弘景 撰"),
("胸中 苦闷痛者", "胸中苦闷痛者"),
("头痛 者,加桂心二两", "头痛者,加桂心二两"),
("疗治明 悉", "疗治明悉"),
("阴旦者,扶 阴之方", "阴旦者,扶阴之方"),
]
# ── A 类:药精条分隔规范化(味辛条脱分隔,其余四条有分隔;体例统一)──────
JING_SEP = [
("味辛皆属木,桂琅玕为之主。生姜伏龙肝为火;附子阳起石为土;细辛礜石为金;干姜雄黄为水。",
"味辛皆属木,桂 琅玕为之主。生姜 伏龙肝为火;附子 阳起石为土;细辛 礜石为金;干姜 雄黄为水。",
"同书「味咸/甘/酸/苦」四条皆以空格分列草木—金石配对,本条空格脱失,据体例补"),
]
# ── B 类:药味行脱分隔空格补入 ────────────────────────────────────────
SEP_FIXES = [
("薯蓣 旋覆花竹叶各一两", "薯蓣 旋覆花 竹叶各一两",
"右七味而药味行仅六词,「旋覆花」「竹叶」间脱分隔空格"),
("细辛 竹叶甘草炙,各一两", "细辛 竹叶 甘草炙,各一两",
"右七味而药味行仅六词,「竹叶」「甘草炙」间脱分隔空格"),
("五味子半升半夏半升", "五味子半升 半夏半升",
"右八味而药味行仅七词,「五味子半升」「半夏半升」间脱分隔空格"),
("皂矾 石英雄黄各一两", "皂矾 石英 雄黄各一两",
"右七味金石方,「石英」「雄黄」间脱分隔空格;本书药精表「甘草炙 石英为火」「干姜 雄黄为水」,"
"与草木行(甘草炙、干姜)对位正合"),
]
# ── C 类:讹字修正(依据逐条留痕;本书为主,参校本仅作旁证)─────────────
FIXES = [
("心德在奐", "心德在耎",
f"本书五脏德性并列(肝德在散、脾德在缓、肺德在收、肾德在坚)皆一字形容,"
f"「奐」「耎」形近;{REF_NAME}作「心德在耎」;《素问·脏气法时论》「心欲软」,耎即软弱之义"),
("腑善瘈", "胻善瘛",
f"《灵枢·五邪》「邪在肝……胻善瘛,节时肿」,本书下文正作「节时肿」「脚下痛」;"
f"「腑」为「胻」之形近讹字(同月旁,府/行形近),胻即胫;{OLD_NAME}作「善瘛」而夺「胻」"),
("懊侬", "懊憹",
f"「懊憹」为古医书联绵词,本书前后两处并见,{REF_NAME}亦作「懊憹」;「侬」「憹」形近致误"),
("白戴浆", "白酨浆",
f"{REF_NAME}作「白酨浆」;《金匮要略》瓜蒌薤白半夏汤用「白酒」,"
f"《外台秘要》谓白酒即白酨浆,「酨」为古酒浆名,「戴」形近致误"),
("白截浆", "白酨浆",
f"{REF_NAME}作「白酨浆」(本书他处亦作「白酨浆」),「截」「酨」形近致误"),
("磐石", "礜石",
f"本书药精表金石配对作「细辛 礜石为金」;《本经》礜石味辛大热,正合木中金之位。"
f"同一药本书三处异形(磐石/舜石/碧石),互校定为「礜石」,「磐」形近致误"),
("舜石", "礜石",
f"小补肺汤散金石方与草木方「细辛」对位,据本书药精表「细辛 礜石为金」;「舜」形近致误"),
("碧石", "礜石",
f"大补肺汤散金石方与草木方「细辛」对位,同上;「碧」形近致误"),
("黄蓍", "黄耆",
f"「蓍」为蓍草,非入药;{REF_NAME}作「黄耆」;本书下文「以黄耆为主」用字自证"),
("大朱乌汤", "大朱鸟汤",
f"本书上下篇并见「小朱鸟汤」「朱鸟者,清滋之方」,{REF_NAME}作「大朱鸟汤」;"
f"「鸟」「乌」形近致误"),
("皂角刮去皮絃", "皂角刮去皮弦",
f"{REF_NAME}作「皮弦」;「絃」为「弦」之异体,取通行字(与旧入库本同例)"),
# —— 以下 4 条据用户 2026-09-16 核对原书结果回填 ——
("虚则目眩,无所见", "虚则目䀮䀮无所见",
"**用户核对原书**:当作「目䀮䀮无所见」。《素问·脏气法时论》「虚则目䀮䀮无所见」;"
f"{REF_NAME}夺作「目无所见」,「眩」为「䀮䀮」之扫描讹字"),
("脉虚而殃", "脉虚而𫘝",
"**用户核对原书**:「𫘝」为「駃」之简化字形(駃通「快」),本书以「𫘝」为「快」;"
"「殃」为扫描讹字(参校本作「脉虚而快」)"),
("脉爽而驶", "脉爽而𫘝",
"**用户核对原书**:同作「𫘝」(参校本作「脉软而快」,本书用字为「𫘝」)"),
("脉驶者", "脉𫘝者",
"**用户核对原书**:确作「脉𫘝者」——「𫘝」(駃之简化字形)为中国古文**通假字**,通「快」;"
"与小补肾汤「脉𫘝者」、大补肾汤「脉爽而𫘝者」同字例(参校本作「脉快者」)"),
("邪气结闼", "邪气结閟",
"**用户核对原书**:「閟」通「闭」,「闼」为形近讹字"),
]
# ── E 类:存疑(原文一律不改)──────────────────────────────────────
SUSPECTS = [] # 原 16 条存疑经用户 2026-09-16/09-16(二) 两轮核对原书,已全部判决
# (据核改 5 条见 C 类;核为无误 11 条见 VERIFIED),本表留空——
# 后续若新发现存疑项,按 (正则, 说明) 追加于此。
# ── G 类:人工核验记录(用户 2026-09-16 核对原书的结果)──────────────
# (核验判决, 行锚片段, 说明)
VERIFIED = [
("改", "虚则目眩,无所见", "核为「目䀮䀮无所见」,已回填清洗版(见 C 类明细)"),
("改", "脉虚而殃", "核为「脉虚而𫘝」(𫘝通「快」),已回填"),
("改", "脉爽而驶", "核为「脉爽而𫘝」,已回填"),
("改", "邪气结闼气分", "核为「邪气结閟气分」(閟通「闭」),已回填"),
("改", "脉驶者",
"核为「脉𫘝者」(𫘝为古文通假字,通「快」)——与 211/257 同字例,已回填"),
("无误", "此篇所列大泻汤散法,悉是小方加母脏泻方", "原书即作此段,文义自洽,原样保留"),
("无误", "附子三枚,炮 生姜三两,切", "原书确作「附子三枚」,原样保留"),
("无误", "生地三两,切 茯苓六两 旋覆花三两 藿三两", "原书确作「生地」「藿」,原样保留"),
("无误", "大枣十五枚,去核", "原书确作「大枣十五枚」「生姜二两」,原样保留"),
("无误", "人参三两 附子二大枚,炮 竹叶三两", "原书版式即此(「去核擘」居后),原样保留"),
("无误", "需用者六十一首", "原书确作「六十一首」,原样保留"),
("无误", "黄耆五两 人参 桂枝 生姜各三两", "大阳旦汤药行八味(含「饴一升」)而末作「右七味」,原书如是,原样保留"),
("无误", "大补心散:海蛤 理石 雄黄 硇砂 姜石 曾青 卤硷", "原书用「卤硷」(硷为碱之异体),原样保留"),
("无误", "卤硷 曾青 姜石 硇砂各三两", "同上,原样保留"),
("无误", "小泻脾汤散:治脾气实,身重不胜",
"原书确作「附子、生姜、甘草」(与本书金石方「阳起石、伏龙肝、石膏」对位,内部自洽),"
"参校本作「干姜」系传本异文,原样保留"),
("无误", "熨耳以通心气:治梦魇不寤者方",
"原书确作「熨耳以通心气」;篇末「右五方」指前五方(点眼、着舌、启咽、吹鼻、灌耳),"
"熨耳为另出之方,原样保留"),
]
# ── F 类:版本差异/体例(不校改)────────────────────────────────────
VERSION_NOTES = [
("体例·右/上", "竖排本作「右X味」(右三味、右六味……),网络横排本作「上三味」;"
"**用户指令以本书为准,全书不改**(竖排古籍体例,非讹字)。"
"又本书方末一律作「右X味」,仅两处通论文字作「上四味」「上二味」(指上文药味,"
"非方末计数),一仍其旧"),
("书名", "本书题《辅行诀五**脏**用药法要》(钱超尘考订书名亦作「五脏」);"
"旧入库参考本与参校本作《辅行诀**脏腑**用药法要》"),
("本书特有·金石方", "每首五脏方(含救误、劳损方)之后另附同构金石药方(琅玕、雄黄、曾青、云母、凝水石、"
"硝石、白垩土、禹粮石、代赭石、白矾、石膏、乳石、磁石、石英、阳起石、伏龙肝、石绿、"
"皂矾、赤石脂、丹砂、硫黄、黄土、滑石、芒硝、礜石等),与「二十五味药精」"
"草木—金石配对一一相应,为本书独有内容(旧本、参校本皆无)"),
("本书特有·救误方", "救误泻五脏方按小、大各一首(共 10 首),方名下附编者说明"
"(据《金匮要略》《神农本草经集注》《外台秘要》引《古今录验》《千金方》、"
"张大昌《处方正范》遗稿补)"),
("本书特有·附录", "附录一「拟补心兼属土火金石补泻方四首」(铁落、石胆、石蜜、朴硝、戎盐、矾石、海蛤、"
"理石、雄黄、姜石、硇砂、曾青、卤碱);"
"附录二「从火论心草木金石小补泻汤散四首」(小泻心汤散、小补心汤散之草木方与金石方各一首,"
"题称四首即此)"),
("图版缺失", "《汤液经法图》及图注(「左阳进为补,其数七……右阴退为泻,其数六……」)本电子本无图;"
"原扫描本图版位置仅存本地路径引用,图与图注待补"),
("无校注篇", "旧入库参考本、参校本皆有「校注」五条(耎意为祛弱、白浆/白酨浆、大泻脾汤药味、分即两、"
"二与六草书形近),本书无校注篇;正文中另有「(据某书补)」「(上四十字,藏经洞卷子"
"传抄本空缺,为笔者据文义所补)」等编者按语 8 处"),
("方数体例", "《汤液经法》方数:本书作「为方亦三百六十五首」"
"(上品百二十首+中品百二十首+下品百二十五首=三百六十五),与本卷「三百六十五味」相应;"
"参校本作「三百六十首」(百二十+百二十+百二十)。两本各自自洽,不改"),
("用字体例/罕用字形", "本书以「𫘝」(U+2B61D,駃之简化字形,通「快」)作「快」"
"(脉𫘝、脉爽而𫘝),而旧入库本、参校本作「快」;"
"另有「䀮」(U+402E,目䀮䀮=目巟巟)等罕用字形,检索时须用原字(以「快」"
"或「目眩」检索均不能命中);用「闷」(大小便闷)而旧本或作「闭」;"
"「日再」/「日二」、「栝蒌」/「栝楼」、「萸肉」/「山萸肉」、「术」/「白术」、"
"「硷」/「碱」、「即济」/「既济」等用字互异,本阶段异体与用字不校,只登记"),
]
# ── 结构识别 ─────────────────────────────────────────────────────────
SECTIONS = [
(r"^# 辅行诀五脏用药法要$", "序·题名"),
(r"^梁·华阳隐居", "序·撰者"),
(r"^隐居曰:凡学道辈", "序·隐居曰"),
(r"^## 辨肝脏病证文并方", "上卷·辨肝脏病证文并方"),
(r"^## 辨心脏病证文并方", "上卷·辨心脏病证文并方"),
(r"^心胞气实者", "上卷·辨心胞病证文(篇题缺)"),
(r"^## 辨脾脏病证文并方", "上卷·辨脾脏病证文并方"),
(r"^## 辨肺脏病证文并方", "上卷·辨肺脏病证文并方"),
(r"^## 辨肾脏病证文并方", "上卷·辨肾脏病证文并方"),
(r"^此篇所列大泻汤散法,悉是小方加母脏", "上卷·五脏大小补泻汤散法通论"),
(r"^又有泻方五首", "上卷·救误泻五脏方(篇题缺)"),
(r"^此篇所列大泻汤散法,上二味是本君臣", "上卷·救误泻方通论"),
(r"^陶云:经方有救诸劳损病方", "下卷·救五脏诸劳损病方(引)"),
(r"^治疗劳损之方", "下卷·救五脏诸劳损病方(小方五首)"),
(r"^此篇所列诸劳损补法所治", "下卷·救五脏诸劳损病方(通论)"),
(r"^陶云:经云:毒药攻邪", "下卷·汤液经法(引)"),
(r"^若欲作大汤散者", "下卷·汤液经法(大汤加法)"),
(r"^陶隐居云:依《神农本经》", "下卷·汤液经法(二十五味药精)"),
(r"^味辛皆属木", "下卷·汤液经法(二十五味药精条文)"),
(r"^此二十五味", "下卷·汤液经法(药精结语)"),
(r"^又有药十三种", "下卷·汤液经法(十三种药)"),
(r"^经云:主于补泻者为君", "下卷·汤液经法(君臣佐使)"),
(r"^陶隐居曰:此图乃", "下卷·汤液经法(图注)"),
(r"^弘景曰:外感天行", "下卷·二旦四神大小汤(篇题缺)"),
(r"^弘景曰:阳旦者", "下卷·二旦四神汤义(篇题缺)"),
(r"^陶隐居云:中恶卒死者", "下卷·救五脏中恶卒死方(篇题缺)"),
(r"^上五方,乃神仙救急之道", "下卷·救急五方结语"),
(r"^附:拟补心兼属土火金石补泻方四首", "附录一·拟补心兼属土火金石补泻方四首"),
(r"^附:从火论心草木金石小补泻汤散四首", "附录二·从火论心草木金石小补泻汤散四首"),
]
FANG_RE = re.compile(r"^(?:救误|养生|调中|建中|凝息|固元)?(?:小|大)?[\u4e00-\u9fff()、]{2,12}?(?:汤散?|散|方)[::]")
OPENERS = re.compile(r"^(?:点眼|着舌|启咽|吹鼻|灌耳|熨耳)[\u4e00-\u9fff]{1,6}[::]")
JINSHI = re.compile(r"^[\u4e00-\u9fff]{1,5}(?:各|)[一二三四五六七八九十百]+(?:两|升|合|枚|把|斤|握|茎|锭|大枚|钱匕)")
CN = {"一":1,"二":2,"三":3,"四":4,"五":5,"六":6,"七":7,"八":8,"九":9,"十":10}
def clean(raw):
events = []
raw_lines = raw.replace("\r\n", "\n").split("\n")
latex_starts = {b[0]: b for b in LATEX_BLOCKS}
# 1) 接合/拆行(行号锚定 + 原文校验)
units, i = [], 0
while i < len(raw_lines):
cur = raw_lines[i]
lineno = i + 1
s = cur.strip()
if lineno in latex_starts:
_st, _en, _rep, _why, _anchor = latex_starts[lineno]
block = "\n".join(raw_lines[lineno - 1:_en])
if _anchor not in block or "$$" not in block:
raise SystemExit(f"LaTeX 块锚校验失败:raw{lineno}–{_en}")
units.append(_rep)
events.append(dict(行=lineno, 类型="A版式残留",
原="LaTeX 公式化残留(\\mathrm/\\text/\\begin{array})",
改=_rep, 依据=_why + ";与本方「右X味」枚数、药名数核对相符"))
i = _en
continue
if lineno in LINE_REPLACE:
anchor, newtext = LINE_REPLACE[lineno]
if anchor not in s:
raise SystemExit(f"单行替换锚校验失败:raw{lineno}")
units.append(newtext)
events.append(dict(行=lineno, 类型="A版式残留", 原=s[:60], 改="图版缺失注记",
依据="本地 Windows 路径不可用、图本体未提供;旧入库本亦缺此图,待补"))
i += 1
continue
if lineno in SPLITS:
mark = SPLITS[lineno]
if mark not in s:
raise SystemExit(f"拆行锚校验失败:raw{lineno} 未含「{mark}」")
head, tail = s.split(mark, 1)
head, tail = head.strip(), tail.strip()
units.append(head)
events.append(dict(行=lineno, 类型="B断行接合", 原=s[:60],
改=f"{head[:40]} / {mark}{tail[:20]}",
依据="药味行与煎服法误并一行,据全书体例拆为两行"))
units.append(mark + tail)
i += 1
continue
if lineno in JOINS:
joiner, anchor = JOINS[lineno]
if not s.startswith(anchor):
raise SystemExit(f"接合锚校验失败:raw{lineno} 应为「{anchor}…」,实为「{s[:20]}」")
while units and not units[-1].strip():
units.pop()
if not units:
raise SystemExit(f"接合失败:raw{lineno} 之前无正文行")
prev = units.pop()
units.append(prev + joiner + s)
events.append(dict(行=lineno, 类型="B断行接合", 原=f"…{prev[-24:]} / {s[:24]}",
改=f"…{(prev + joiner + s)[-40:]}",
依据="版心换页/换行致句中截断,接合后语义完整(与「右X味」枚数、药名数核对相符)"))
i += 1
continue
units.append(s)
i += 1
# 2) 行尾空格清理(汇总登记)
n_trail = sum(1 for l in raw_lines if l != l.rstrip())
events.append(dict(类型="A版式残留", 原=f"行尾空格 {n_trail} 行", 改="已删",
依据="扫描/PDF 转换残留,逐行 rstrip"))
# 3) 逐条修正(在接合后的段落上)
# 段落序号 -> 成文行号(成文以空行分隔,第 j 个非空段落在第 2j+1 行)
line_map = {k: 2 * j + 1 for j, k in enumerate([i for i, u in enumerate(units) if u.strip()])}
out = []
for k, line in enumerate(units):
ln = line_map.get(k)
def ev(**kw):
e = dict(kw)
if ln is not None:
e["行"] = ln
events.append(e)
orig = line
for bad, good in STRAY_SPACES:
if bad in line:
ev(类型="A版式残留", 原=bad, 改=good, 依据="句中误入空格")
line = line.replace(bad, good)
for bad, good, why in FIXES:
if bad in line:
ev(类型="C讹字修正", 原=bad, 改=good, 依据=why)
line = line.replace(bad, good)
for bad, good, why in JING_SEP:
if bad in line:
ev(类型="A版式残留", 原=bad[:24] + "…", 改=good[:30] + "…", 依据=why)
line = line.replace(bad, good)
if line.startswith("味") and "皆属" in line: # 药精条:标点后误空格
line = re.sub(r"(?<=[,。;:])\s+", "", line)
for bad, good, why in SEP_FIXES:
if bad in line:
ev(类型="B脱分隔", 原=bad, 改=good, 依据=why)
line = line.replace(bad, good)
d = re.sub(r"([\u4e00-\u9fff])\s*:\s*", r"\1:", line)
if d != line:
ev(类型="D标点", 原="半角冒号", 改="全角冒号", 依据="标点规范化")
line = d
out.append(line)
text = "\n\n".join([l for l in out if l]) + "\n"
# 3) E 存疑定位
sus, clines = [], text.split("\n")
for i, l in enumerate(clines, start=1):
for pat, why in SUSPECTS:
if re.search(pat, l):
sus.append({"行": i, "片段": l[:60], "说明": why})
seen, uniq = set(), []
for s in sus:
key = (s["行"], s["说明"])
if key not in seen:
seen.add(key); uniq.append(s)
return text, events, uniq
def is_drug_like(l):
"""药味行/金石行:以空格分列药名,至少一处含量词或「各等分」。"""
l = l.strip()
toks = [t for t in re.split(r"\s+", l) if t]
if len(toks) < 2 or not re.match(r"^[\u4e00-\u9fff()、]{1,8}", toks[0]):
return False
return any(re.search(r"(?:各|)[一二三四五六七八九十百]+(?:两|升|合|枚|把|斤|握|茎|锭|大枚|钱匕)|各?等分", t)
for t in toks)
def selfcheck(text):
"""自检:C 讹字残留、味数核对、句中残留空格清单。"""
errs, warn = [], []
for bad, good, _ in FIXES:
if bad in text and bad != good:
errs.append(f"C 规则未生效:「{bad}」仍存于成文")
lines = text.split("\n")
for i, l in enumerate(lines):
m = re.search(r"^右(?:方|药)?([一二三四五六七八九十]+)味", l)
if m and "以" in l[:10]:
j = i - 1
while j >= 0 and not lines[j].strip():
j -= 1
toks = [t for t in re.split(r"\s+", lines[j]) if t]
exp = CN[m.group(1)]
if "饴一升" in lines[j]: # 大阳旦汤:饴为辅料,书本末文仍作「右七味」
exp += 1
if len(toks) != exp:
if "各一两" in lines[j] and len(toks) == exp + 1: # 附「余三味俱作一两」类不在此列
pass
errs.append(f"味数不符(行{i+1}):右{m.group(1)}味,药行 {len(toks)} 词 → {lines[j][:60]}")
space_lines = [(i + 1, l) for i, l in enumerate(lines)
if re.search(r"[\u4e00-\u9fff]\s[\u4e00-\u9fff]", l)]
for ln, l in space_lines:
if not (is_drug_like(l) or l.startswith("味") or "·" in l):
warn.append(f"行{ln} 仍含词间空格(非药味行/金石行,请人工确认):{l[:70]}")
return errs, warn, space_lines
def parse_struct(text):
lines = text.split("\n")
nodes = []
for i, l in enumerate(lines, start=1):
for pat, name in SECTIONS:
if re.match(pat, l.strip()):
nodes.append({"篇": name, "起始行": i})
break
for n, node in enumerate(nodes):
node["结束行"] = nodes[n + 1]["起始行"] - 1 if n + 1 < len(nodes) else len(lines)
fangs = []
for i, l in enumerate(lines, start=1):
s = l.strip()
if s.startswith("#"):
continue
if FANG_RE.match(s) or OPENERS.match(s):
owner = next((n["篇"] for n in nodes if n["起始行"] <= i <= n["结束行"]), "(篇外)")
fangs.append({"篇": owner, "方名": re.sub(r"[::].*$", "", s), "起始行": i})
for n, f in enumerate(fangs):
f["结束行"] = fangs[n + 1]["起始行"] - 1 if n + 1 < len(fangs) else len(lines)
return nodes, fangs
def main():
raw = open(SRC, encoding="utf-8").read()
cleaned, events, sus = clean(raw)
errs, warn, space_lines = selfcheck(cleaned)
nodes, fangs = parse_struct(cleaned)
def wf(path, content):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content); f.flush(); os.fsync(f.fileno())
wf(OUT_MD, cleaned)
struct = {
"来源文件": os.path.basename(SRC), "来源文件md5": hashlib.md5(raw.encode()).hexdigest(),
"清洗版文件": os.path.basename(OUT_MD), "清洗版md5": hashlib.md5(cleaned.encode()).hexdigest(),
"定位": "《辅行诀五脏用药法要》基础文本(底本):印刷书籍扫描本;用户 2026-09-16 指令为准",
"参校本": REF_NAME,
"体例说明": "行号均指清洗版;竖排本作「右X味」,本书为准,不改作「上」;本文件仅作清理配套定位索引,"
"未做药性释义抽取与跨库关联",
"篇": nodes, "方": fangs,
}
wf(OUT_STRUCT, json.dumps(struct, ensure_ascii=False, indent=2) + "\n")
owner = lambda ln: next((n["篇"] for n in nodes if n["起始行"] <= ln <= n["结束行"]), "(篇外)")
per, per_sus = {}, {}
for e in events:
per.setdefault(owner(e["行"]) if "行" in e else "(全局·脚本级)", []).append(e)
for s in sus:
per_sus.setdefault(owner(s["行"]), []).append(s)
c = lambda t: sum(1 for e in events if e["类型"].startswith(t))
rep = ["# 《辅行诀五脏用药法要》印刷本(基础文本)— 清洗报告", "",
"> 📌 **状态:暂定标准范本(baseline)**——用户 2026-09-16 认可本报告输出的"
"`辅行诀五脏用药法要_基础文本_清洗版.md` 作为《辅行诀五脏用药法要》的标准文本,"
"**如后续发现错漏,再按本脚本规则表增改后重跑刷新**(源文件 `01_来源数据/` 永不改动)。",
"> 用户 2026-09-16 指令:**印刷书籍扫描本优于此前入库的古本原文与参校本,定为基础文本(底本)**;",
"> 参校本仅在判断扫描讹字、脱字、脱分隔时作辅助证据,不作版本改写。",
"> 竖排本作「右X味」(网络横排本作「上X味」)——**以本书为准,一律不改**。",
"> 本阶段仍只做数据清理,不做药性释义抽取、跨库关联与主题分析。", "",
"## 0 概览", "",
f"- 来源:`01_来源数据/{os.path.basename(SRC)}`(md5 `{struct['来源文件md5']}`)",
f"- 基础文本:`02_加工数据/{os.path.basename(OUT_MD)}`(md5 `{struct['清洗版md5']}`)",
f"- 参校本:{REF_NAME}",
f"- 字符数:{len(raw)} → {len(cleaned)};行数:{raw.count(chr(10))+1} → {cleaned.count(chr(10))+1}",
f"- **baseline(暂定标准范本)**:2026-09-16 用户认可;清洗版 md5 `{struct['清洗版md5']}`"
f"(变更须在文末更新日志登记)",
f"- 结构:{len(nodes)} 个篇段 / {len(fangs)} 首方;行号锚定接合 {len(JOINS)} 处、拆行 {len(SPLITS)} 处",
"", "| 清理类别 | 处数 | 说明 |", "|---------|------|------|",
f"| A 版式残留 | {c('A')} | 行首行尾空格(273 行)、句中误入空格 6 处、LaTeX 残留 4 块、"
f"图片路径 1 处、药精条分隔缺失 1 处 |",
f"| B 断行接合/脱分隔 | {c('B')} | 换页换行接合 {len(JOINS)} 处、药味行脱分隔 4 处、误并行拆开 {len(SPLITS)} 处 |",
f"| C 讹字修正 | {c('C')} | 每条附核校依据(本书内证+参校本+同类文献),见逐篇明细 |",
f"| D 标点规范化 | {c('D')} | 半角标点改全角 |",
f"| E 存疑(未改原文) | {len(sus)} | 原 16 条存疑经用户两轮核对原书**已全部核验完毕**"
f"(据核改 5 · 核为无误 11),现存疑册为空 |",
f"| F 版本差异/体例(未改) | {len(VERSION_NOTES)} | 本书特有内容与用字体例,不作校改 |",
f"| G 人工核验(已回填) | 改 {sum(1 for v in VERIFIED if v[0].startswith('改'))} · 无误 "
f"{sum(1 for v in VERIFIED if v[0]=='无误')} | 用户 2026-09-16 核对原书结果,见第 3 节 |",
"", "### 各篇清理量一览", "",
"| 卷·篇 | 行范围 | 方数 | A | B | C | D | E 存疑 |",
"|-------|--------|------|---|---|---|---|--------|"]
for n in nodes:
evs = per.get(n["篇"], [])
cc = lambda t: sum(1 for e in evs if e["类型"].startswith(t))
_ = cc
fn = sum(1 for f in fangs if f["篇"] == n["篇"])
rep.append(f"| {n['篇']} | {n['起始行']}–{n['结束行']} | {fn} | {cc('A')} | {cc('B')} | "
f"{cc('C')} | {cc('D')} | {len(per_sus.get(n['篇'], []))} |")
rep += ["", "---", "", "## 1 逐篇核对明细(按卷/篇列明)", ""]
for n in nodes:
evs, ss = per.get(n["篇"], []), per_sus.get(n["篇"], [])
rep += [f"### {n['篇']}(行 {n['起始行']}–{n['结束行']})", "",
f"- 清理量:A {sum(1 for e in evs if e['类型'].startswith('A'))} / "
f"B {sum(1 for e in evs if e['类型'].startswith('B'))} / "
f"C {sum(1 for e in evs if e['类型'].startswith('C'))} / "
f"D {sum(1 for e in evs if e['类型'].startswith('D'))} / 存疑 {len(ss)}", ""]
if evs:
rep += ["| 行 | 类别 | 原文 | 处理后 | 核校依据 |", "|----|------|------|--------|---------|"]
for e in evs:
rep.append(f"| {e.get('行','—')} | {e['类型']} | {e['原'].replace('|','|')} | "
f"{e['改'].replace('|','|')} | {e['依据'].replace('|','|')} |")
rep.append("")
if ss:
rep += ["存疑项:", "", "| 行 | 片段 | 说明 |", "|----|------|------|"]
for s in ss:
rep.append(f"| {s['行']} | {s['片段'].replace('|','|')} | {s['说明'].replace('|','|')} |")
rep.append("")
if not evs and not ss:
rep += ["- 本篇无需修正条目。", ""]
glob = per.get("(全局·脚本级)", [])
if glob:
rep += ["---", "", "### 全局清理项(不属具体篇段)", "",
"| 类别 | 原文 | 处理后 | 核校依据 |", "|------|------|--------|---------|"]
for e in glob:
rep.append(f"| {e['类型']} | {e['原'].replace('|','|')} | {e['改'].replace('|','|')} | "
f"{e['依据'].replace('|','|')} |")
rep.append("")
rep += ["---", "", "## 2 自检结果(脚本自动)", "",
f"- C 类讹字残留检查:{'通过(无残留)' if not errs else '**未通过**'}",
f"- 「右X味」枚数 vs 药味行词数核对:{'全部相符' if not any('味数不符' in e for e in errs) else '**有不符**'}",
f"- 句中残留空格检查:{len(space_lines)} 行含药味行分隔空格(正常,见下);"
f"{len(warn)} 行需人工确认"]
for e in errs:
rep.append(f" - ⚠ {e}")
for w in warn:
rep.append(f" - ⚠ {w}")
rep += ["", "含分隔空格的药味行/金石行清单(抽检用,非错误):", "",
"| 行 | 内容 |", "|----|------|"]
for ln, l in space_lines:
if JINSHI.match(l) or l.startswith("味"):
rep.append(f"| {ln} | {l[:70]} |")
rep += ["", "---", "", "## 3 人工核验回填记录(用户 2026-09-16 核对原书)", "",
"「改」=原书当作某字,已回填清洗版(C 类明细中「用户核对原书」条目);"
"「无误」=本来源与原书一致,原样保留,不再列为存疑。", "",
"| 判决 | 清洗版片段 | 核验结果 |", "|------|-----------|---------|"]
for verdict, anchor, note in VERIFIED:
rep.append(f"| {verdict} | {anchor.replace('|','|')} | {note.replace('|','|')} |")
if sus:
rep += ["", "**尚待核对**:", ""]
for s_ in sus:
rep.append(f"- 行 {s_['行']}:{s_['片段'][:50]} —— {s_['说明']}")
else:
rep += ["", "**尚待核对:无**——原 16 条存疑已全部判决并闭环"
"(据核改 5 条已回填,核为无误 11 条原样保留)。", ""]
rep += ["", "## 4 版本差异/体例登记(不校改)", "", "| 类别 | 说明 |", "|------|------|"]
for k, v in VERSION_NOTES:
rep.append(f"| {k} | {v} |")
rep += ["", "## 5 篇段结构(清洗版行号)", "", "| 卷·篇 | 起始行 | 结束行 |", "|-------|--------|--------|"]
for n in nodes:
rep.append(f"| {n['篇']} | {n['起始行']} | {n['结束行']} |")
rep += ["", "## 6 方目(清洗版行号)", "", "| 篇 | 方名 | 起始行 | 结束行 |", "|----|------|--------|--------|"]
for f in fangs:
rep.append(f"| {f['篇']} | {f['方名']} | {f['起始行']} | {f['结束行']} |")
wf(OUT_REPORT, "\n".join(rep) + "\n")
chk = ["# 人工核对清单 — 《辅行诀五脏用药法要》印刷本(基础文本)", "",
"> 用途:需对照纸质原书确认的条目。**按卷/篇列明**,本阶段不改动清洗版正文。",
f"> 参校本:{REF_NAME}。以印刷本为准,参校本仅供查错补漏。",
"> 📌 清洗版已由用户 2026-09-16 认可为**暂定标准范本**;后续如发现错漏,按本节记录方式回填并刷新。",
"> 用户 2026-09-16 两轮核对原书,**16 条存疑已全部判决完毕**:据核改 5 条(已回填清洗版)、"
"核为无误 11 条(原样保留)。", "",
f"## 一、尚待核对({len(sus)} 条)", "",
"| 行 | 篇 | 片段 | 疑正/说明 |", "|----|----|------|-----------|"]
if sus:
for s_ in sus:
chk.append(f"| {s_['行']} | {owner(s_['行'])} | {s_['片段'].replace('|','|')} | "
f"{s_['说明'].replace('|','|')} |")
else:
chk.append("| — | — | — | 无:原 16 条存疑已全部核验完毕(见第二节) |")
chk += ["", "## 二、已人工核验(用户 2026-09-16 核对原书)", "",
"### 2.1 据核改(已回填清洗版)", "",
"| 判决 | 清洗版片段 | 核验结果 |", "|------|-----------|---------|"]
for verdict, anchor, note in VERIFIED:
if verdict.startswith("改"):
chk.append(f"| {verdict} | {anchor.replace('|','|')} | {note.replace('|','|')} |")
chk += ["", "### 2.2 核为无误(原样保留)", "",
"| 判决 | 清洗版片段 | 核验结果 |", "|------|-----------|---------|"]
for verdict, anchor, note in VERIFIED:
if verdict == "无误":
chk.append(f"| 无误 | {anchor.replace('|','|')} | {note.replace('|','|')} |")
chk += ["", "## 三、版本差异/体例登记(不校改,待核原书)", "", "| 类别 | 说明 |", "|------|------|"]
for k, v in VERSION_NOTES:
chk.append(f"| {k} | {v} |")
chk += ["", "## 书目与来源", "",
"- 文本:《辅行诀五脏用药法要》(梁·华阳隐居 陶弘景撰)",
f"- 本次来源:`01_来源数据/{os.path.basename(SRC)}`(印刷书籍扫描本,用户 2026-09-16 提供)",
"- 定位:本库《辅行诀》**基础文本(底本)**,优先于此前入库的古本原文(旧参考本)",
"- 参校本:维基文库本(快照见 `01_来源数据/校核参考/`),仅作查错补漏",
"- 已修正条目(C 类)见 `02_加工数据/印刷本清洗报告.md` 第 1 节逐篇明细;",
" 人工核验回填记录见同报告第 3 节"]
wf(OUT_CHECK, "\n".join(chk) + "\n")
print("基础文本:", OUT_MD, os.path.getsize(OUT_MD), "bytes")
print("结构 :", OUT_STRUCT, os.path.getsize(OUT_STRUCT), "bytes")
print("报告 :", OUT_REPORT, os.path.getsize(OUT_REPORT), "bytes")
print("核对单 :", OUT_CHECK, os.path.getsize(OUT_CHECK), "bytes")
print(f"A {c('A')} | B {c('B')} | C {c('C')} | D {c('D')} | 存疑 {len(sus)} | 版本差异 {len(VERSION_NOTES)}")
print(f"篇 {len(nodes)} | 方 {len(fangs)} | 自检错误 {len(errs)} | 待确认 {len(warn)}")
for e in errs:
print(" ⚠", e)
for w in warn:
print(" ⚠", w)
if __name__ == "__main__":
main()