[编修求助·半成品] 《21世纪大英汉词典-全索引》整理进度与困难

[编修求助·半成品] 《21世纪大英汉词典-全索引》整理进度与困难

《21世纪大英汉词典》内容非常全面,释义也非常准确,词条数也很多,是我用过的最好用的英汉词典,能满足 GRE 和偏门学科对词汇的极限要求。

下面分享一下我以网络流行版本为底本进行修订的一些经验和困难,主要还是向各位大神和论坛用户朋友求助。

即使你没有修订过这本词典,或许你也可以从我这里学到一些成功或失败的经验,对你编修其它词典可能会有一点点的灵感和启发。

0 前情提要

我在修订前发了一篇纯求助帖子,里面记载了我的一些最初的想法和修订的缘由,也在评论区学到了如何上手编修,详情见这篇帖子:21世纪大英汉词典 双向索引 427万词条

1 项目地址

  1. GithubGitHub - RaterChan/21-CUEC-Dict-Compilation: 21世纪大英汉词典编修的资源仓库 · GitHub (由于 Github 限制,此处没有原始的 .mdx 文件)
  2. Quark网盘夸克网盘分享
  3. Google Drivehttps://drive.google.com/drive/folders/1pHXjgecYpFgd_oQarxEMiK76cJtK0RKu

声明: 所有的代码都是使用 Google Gemini (Pro)(我强烈建议你用Gemini因为它就像美国豆包一样,推荐选择Anthropic的Claude或者OpenAI的ChatGPT) 写的,我本身能看懂一些代码,但是自己写不出来。AI 写的代码有些中二病是很正常的(至少对于Gemini是这样)。每个代码文件都有详细完整的解析和注释,这些解析和注释是朴实的,没有中二病,请放心观看。

2 所用材料和所用工具

2.1 词典:《21世纪大英汉词典》

英文名称:The 21st Century Unabridged English-Chinese Dictionary.mdx 文件作者不详,版本不详。

我已将这个包含在我的项目中,上述项目地址有包含 .mdx 文件(Github 链接除外)。

我从这里获取:https://cloud.freemdict.com/index.php/s/pgKcDcbSDTCzXCs?dir=/ENGLISH/Eng-Chi/21st%20Century%20Dictionaries

2.2 Python (IDE+interpreter)

  1. AnacondaDownload Success | Anaconda
  2. PyCharmhttps://www.jetbrains.com/pycharm/) 或者 MS VS Codehttps://code.visualstudio.com/)

2.3 Python库:MDTT

  1. Englishmdtta · PyPI
  2. 中文mdtt/README-zh.md at master · libukai/mdtt · GitHub

2.4 数据库软件:DB Browser for SQLite

下载链接:Downloads - DB Browser for SQLite

2.5 在线 HTML 解析(辅助)

3 词典基本信息

3.1 信息概览与特征

第二步去重后,基本信息统计(basic info.txt)如下:

一共 4,057,426 行,其中:

  1. 值是跳转的,即以 “@@@LINK=” 开头的有 2,920,731 行,占比 71.98%
  2. 独立词头有 3,138,401 个,占比 77.35%
  3. 值是 HTML 的,有 1,136,695 行,占比 28.02%,其中:
    • 3.1 以 <link rel="stylesheet" href="《21世纪大英汉词典》.css"/> 开头的有 652,990
    • 3.2 以 <link href="《21世纪大英汉词典》.css" rel="stylesheet" type="text/css"/> 开头的有 483,705

3.2 数据示例

略,数据实在过于繁杂和凌乱,与其看我写的示例不如打开数据库结合查询代码看上个 10 min,或者跟着我下面的步骤一步步解锁。

3.3 整理思路

原始 .mdx 文件 —[MDTT解包]—> .db 文件 —[DB Browser去重]—> .db 文件 —[消除跳转链接,避免修改过程中产生死链]—> .db 文件 —[清洗乱码字符,主要是“/”]—> .db 文件 —[整理低频键名]—> .db 文件 —[语义级去重,消除重复释义]—> .db 文件 —[…]—> … —[…]—> .db 文件 —[重建跳转链接,节省存储空间]—> .db 文件 —[MDTT打包]—> 成品 .mdx 文件

这里的是理想状况下的步骤,每个步骤难度不一,我并没有严格按照这个步骤(详见下文),有些步骤我也没想那么远。

4 整理过程

4.1 第一步:将 .mdx 文件转换成 .db 文件

4.1.1 解包

详情见中文 README-zh.md21世纪大英汉词典 双向索引 427万词条 的讨论部分。

4.1.2 结果

得到两个文件:

  1. The 21st Century Unabridged English-Chinese Dictionary.db1433176 KB
  2. The 21st Century Unabridged English-Chinese Dictionary.mdx.meta.toml1 KB

可以用 DB Browser 验证,现在 .db 文件有 4,278,399 行记录。

4.2 第二步:去重1(消除完全相同的键值对)

这一步比较简单,直接使用 DB Browser 打开 .db 文件,点击 Execute SQL,粘贴以下代码:

DELETE FROM mdx  
WHERE rowid NOT IN (  
    SELECT MIN(rowid)  
    FROM mdx  
    GROUP BY entry, `paraphrase`  
);

然后点击F5,运行完毕后点击Write changes 即可。

去重后,.db 文件有 4,057,426 条记录。

4.3 第三步:消除跳转链接,“展平”数据库

4.3.1 使用脚本:full_dict.py

4.3.2 问题

使用 DB Browser,输入简单的 SQL 语句即可知道这里面有很多乱码键,比如富含“/”的键,而正常的键反而依赖这些乱码键进行查询的中转,如:

105号元素”的释义是:

@@@LINK=105号元素-105/号/元素-【化学、物理学】105号元素 [亦作 element 105, hahnium]

而后者的释义才是真正的释义:

<link rel="stylesheet" href="《21世纪大英汉词典》.css"/><div class="ex-phrase"><div class="ref"><span class="see_label"></span><span class="main_entry"></span><a class="m" href="entry://unnilpentium">unnilpentium</a> <span class="phonetic">[,ju:nil'pentiəm]</span> </div><ul><li class="wordGroup"><span class="pos">n.</span><span class="def">【化学、物理学】105号元素 [亦作 element 105, hahnium]</span></li></ul></div>

这说明如果无脑去除带“/”等符号的乱码键如“105号元素-105/号/元素-【化学、物理学】105号元素 [亦作 element 105, hahnium]”,则会造成以上查询链条断裂,导致查询“105号元素”返回空结果。

4.3.3 解决

由此可知,我们需要将数据库“展平”,即消除所有跳转链接,把每个键的最终释义给提到自己的“paraphrase”列中。

full_dict.py 执行的就是这样一个功能。其具体的输入输出、注释和全代码逻辑解析在代码文件中详细清晰地写出,打开即可看到,这里篇幅原因不写代码解析。

注意:这样会导致数据库体积暴增,会产生一个 4991492 KB 的新的 .db 文件。执行 full_dict.py 时,注意最后 main 函数的输入输出文件名是否符合你的预期,下同。

4.3.4 此步产出(文件名可能不一样)

  1. full_dict_without_link.db4991492 KB
  2. mdx_cleaner_anomaly.log

用各自的软件打开可验证,.db 文件有 4,301,183 条记录;.log 文件只有1条死链,如下图:

DB Browser 验证,确实如此。

4.4 第四步:去重2(消除同一个键中多个值的语句级相互包含关系)

4.4.1 使用脚本:find_sub_set.py

4.4.2 问题

这个 .mdx 文件最大的问题是重复,比如这样的重复,在数据库中找到“不退让”这个词头,可以看到属于这个词头的有 10 条记录。

如果你的文件和我一模一样,那么请选中行号 12701001270108 这两条记录的释义部分:

1270100.

<link rel="stylesheet" href="《21世纪大英汉词典》.css"/><div class="ex-phrase"><div class="ref"><span class="see_label"></span><span class="main_entry"></span><a class="m" href="entry://hold one's own">hold one's own</a>  </div><ul><li class="wordGroup"><span class="def">坚持自己的立场;坚守住,不退让</span></li></ul></div>

1270108.

<link rel="stylesheet" href="《21世纪大英汉词典》.css"/><div class="ex-phrase"><div class="ref"><span class="see_label"></span><span class="main_entry"></span><a class="m" href="entry://hold one's own">hold one's own</a>  </div><ul><li class="wordGroup"><ul class="ol wordGroup"><li class="wordGroup"><span class="def">坚持自己的立场;坚守住,不退让</span></li><li class="wordGroup"><span class="def">可匹敌,敌得过</span></li><li class="wordGroup"><span class="def">保持力量,不衰退;维持原有健康状况</span></li></ul></li></ul></div>

复制之,粘贴到 HTML在线解析 (如果出现了些许差异,请把 10 条记录的释义部分竖着选取复制,结果一样)进行查看,可以发现有两条释义是部分重复的,存在子集关系。

网页在线解析结果:

可以看到 1270100 释义是 1270108 释义的子集,前者与后者的第一条子释义是一样的,且词头“hold one's own”也是完全一样。需要去重

4.4.3 解决

但是 paraphrase 一列是 HTML 代码,因为 HTML 代码的封闭特性,就算解析后的内容上有重复,其字面上的 HTML 代码未必是有子集关系,所以不能直接进行字符匹配来找子集关系。必须先用一个叫 BeautifulSoup 的 Python 库解析这些 HTML 代码,提取其解析后的真正显示给人类看的文字内容再进行子集关系判断。

find_sub_set.py 实现的就是这样的一个功能。其具体的输入输出、注释和全代码逻辑解析在代码文件中详细清晰地写出,打开即可看到,这里篇幅原因不写代码解析。

注意,这一步会生成一个新的 .db 文件(4957676 KB),请留意存储空间。

4.4.4 此步产出(文件名可能不一样)

  1. full_dict_semantic_cleaned.db4957676 KB
  2. semantic_killed_audit_1.csv93534 KB

用各自的软件打开可验证,.db 文件有 4,242,799 条记录;.csv 文件有 58,384 条记录(不含表头)。

4.5 第五步:清洗键当中的“/”字符

4.5.1 使用脚本:remove_slash_1.py

4.5.2 问题

当你第一次打开 .db 文件的时候,你可能已经发现了,这本词典在制作的过程中建立了太多的乱码键,即将一个中文或英文句子用“/”分割成了很多份,这可能是原作者建立索引的时候代码分割失误所造成的垃圾。如:

1. 伦斯/劳伦斯/(/1901/~/1958/,/美国/物理/理学/学家/物理学/理学家/物理学家/,/1939/年/获/诺贝/贝尔/物理/理学/诺贝尔/物理学/诺贝尔物理学奖/)  
2. 因此1-以那一点上-以/那/一点/上-以那(或这)一点上-以/那/(/或/这/)/一点/上  
3. 派/人/看守/着  
4. ...

4.5.3 解决

但是这些不能全部删除,要不然反查功能就没了,所以为了保住反查功能,我们只能进行“屎里淘金”,尽可能地先恢复其原本的样貌。

remove_slash_1.py 实现的就是这样的一个功能。其具体的输入输出、注释和全代码逻辑解析在代码文件中详细清晰地写出,打开即可看到,这里篇幅原因不写代码解析。

注意,这一步会生成一个新的 .db 文件(3759720 KB),请留意存储空间。

4.5.4 此步产出(文件名可能不一样)

  1. full_dict_slash_removed_1.db3759720 KB
  2. slash_removed_1_audit.csv164408 KB

用各自的软件打开可验证,.db 文件有 3,193,939 条记录;.csv 文件有 1,048,575 条记录(不含表头)。

4.6 第六步:去重3(消除同一个键中多个值的词汇级相互包含关系)

4.6.1使用脚本:1. fragment_clean_preview.py; 2. fragment_clean_execute.py

4.6.2 问题

这一步是第四步的历史遗留问题。由于我对这本词典的重复的模式没有深入研究,加之对数据库、Python 编程都不太懂,所以我基本上在和 AI 问答过程中都是懵的,AI 写的代码的漏洞或者逻辑不全的地方我一开始没发现。

可是我们第四步不是去过重了吗?是,也不是。第四步的程序有问题,它的处理最小单位是 HTML 代码经过 BeautifulSoup 解析后的文本句子,这在“子集在超集的第一条”的时候是生效的,但是子集如果在超集的后面几条记录,那是完全匹配不到的。

这么说非常的抽象,抽象就对了,我一开始也没想这么细。

我们先回过头来看第四步的程序能处理的情况:

这在 find_sub_set.py 中被解析为:

1270100: {"hold one's own 坚持自己的立场;坚守住,不退让"}

1270108: {"hold one's own 坚持自己的立场; 坚守住,不退让 可匹敌,敌得过 保持力量,不衰退;维持原有健康状况"}

在对比的时候会比较这两个字符串,12701001270108 的子串吗?True。则 1270100 被过滤掉。

那么什么情况下这个程序失效呢?我们来看另外一个例子。

直接在数据库中找到“1/4平方英里土地”这一词条,它有两个值,分别是:

<link rel="stylesheet" href="《21世纪大英汉词典》.css"/><div class="ex-phrase"><div class="ref"><span class="see_label"></span><span class="main_entry"></span><a class="m" href="entry://quarter section">quarter section</a> <a class="m" href="entry://quarter sections">quarter sections</a>  </div><ul><li class="wordGroup"><ul class="ol wordGroup"><li class="wordGroup"><span class="def">[美国西部方言]四分之一平方英里的一块地</span></li><li class="wordGroup"><span class="def">1/4平方英里土地(合160英亩)</span></li></ul></li></ul></div>

<link rel="stylesheet" href="《21世纪大英汉词典》.css"/><div class="ex-phrase"><div class="ref"><span class="see_label"></span><span class="main_entry"></span><a class="m" href="entry://quarter section">quarter section</a> <a class="m" href="entry://quarter sections">quarter sections</a>  </div><ul><li class="wordGroup"><span class="def">1/4平方英里土地(合160英亩)</span></li></ul></div>

鼠标竖着选中这两个单元格,复制之,粘贴到 HTML在线解析 中(或者在词典软件中直接搜索“1/4平方英里土地(合160英亩)”,效果一致),可以看到第二条短的解释(子集)“1/4平方英里土地(合160英亩)”是第一条长的解释(超集)的第二条解释。

find_sub_set.py(第四步所用程序)的 BeautifulSoup 解析 HTML 代码时,它的解析结果是:

  1. 子集(短的解释,上图的第二条记录):{quarter section quarter sections 1/4平方英里土地(合160英亩)}
  2. 超集(长的解释,上图的第一条记录):{quarter section quarter sections [美国西部方言]四分之一平方英里的一块地 1/4平方英里土地(合160英亩)}

问题就出在这里:因为超集在中间插入了 “[美国西部方言]...” 这半句话,导致子集原本相连的文字被截断了。这就像问:“我是好人” 包含 “我是人” 吗?

find_sub_set.py(第四步所用程序)运用了 Python 的“if A in B”逻辑,而这个逻辑对以上问题的回答是:False。所以,find_sub_set.py 解决不了这个问题。

4.6.3 解决

由上可知,问题出在 find_sub_set.py 处理的最小单位不够小,所以将 BeautifulSoup 解析 HTML 代码得到的语句接着打碎,变成这样的集合:

  1. 短释义集合(子集) = {"quarter section", "quarter sections", "1/4平方英里土地(合160英亩)"}
  2. 长释义集合(超集) = {"quarter section", "quarter sections", "[美国西部方言]四分之一平方英里的一块地", "1/4平方英里土地(合160英亩)"}

这样再进行数学上的集合运算就可以找到所有重复的地方。 fragment_clean_preview.pyfragment_clean_execute.py 实现的就是这样的一个功能。其具体的输入输出、注释和全代码逻辑解析在代码文件中详细清晰地写出,打开即可看到,这里篇幅原因不写代码解析。

理论上 fragment_clean_preview.pyfragment_clean_execute.py 是可以直接在第四步使用直接解决全部问题的,但是我没试过,我是前面做的不充分这一步补做的。

注意:这一步会产生两个新的大文件,一个新的 .db 文件(2404508 KB)和一个新的 .csv 文件(4XXXXXX KB),没错,日志文件比数据库文件本身还大了将近一倍

4.6.4 此步产出(文件名可能不一样)

  1. full_dict_fragment_cleaned.db2404508 KB
  2. fragment_clean_execute_report.csv4XXXXXX KB),此时这个大小已经远远超过 Microsoft Excel 的极限,最方便的方法是将此文件用 DB Browser 打开并导入数据,另存为一个新的数据库,这样方便打开和查看。如果你采用此方法,你得到的fragment_clean_execute_report.db 应该是 4683792 KB。

DB Browser 打开以上两个文件可以验证,full_dict_fragment_cleaned.db2,982,142 条记录,fragment_clean_execute_report.db211,816 条记录。

5 小结

我对《《21世纪大英汉词典》-全索引》进行了初步数据清洗和编修,去除了完全相同的词条,生成了不具有跳转链接的完全词典作为中间体,去除了可以由以 HTML 代码解析后得到的文本内容确定子集关系的重复内容。

以上所有工作花了我大约 5 个小时,包含查阅 MDTT 文档等学习工具文档的时间和与 AI 对话编写提示词和等待回答的时间。

6 当前状态与问题

  1. 数据仍然十分混乱和“肮脏”,尽管乱码中的“/”字符被去除,但是很多键名也就是词头是完全没有意义的,比如:
    • 1.1. 'vjei(z)/ des frandres 【动物】佛兰德牧牛狗(产于比利时)
    • 1.2. (15世纪希腊被土耳其占领后上山坚持斗争的)希腊游击队员游击队游击队员
    • 1.3. black lines designate boundaries on this map
    • 1.4. love的变形-love的变形
    • 1.5. low latin-low latin[亦作 l.l. ]-low latin[亦作 l.l. ]
    • 1.6. 潺潺声-潺潺声-(流水)潺潺声-(流水)潺潺声
    • 1.7. (a) hell on earth1
    • 1.8. = checkered lily-✘ = checker lily

现在的数据仍然会出现如 1.1 那样的被截断的音标和不同于“/”的音标斜杠“”,同时冗长的键几乎不可能在日常查询时用到;同时也可能出现无意义或者未分割完全的键名,比如 1.4 中的“-”,其键名本身就含有无意义的重复;也有像 1.5 这种直接从词典释义截取下来当键名反查的失败案例,不仅有重复,还加了无意义的空格,破坏了原本的释义,更是几乎不可能用到;也有像 1.7 那样词组后面带数字编号的,在数据库一般有连续编号,释义至少有一项是不一样的,应该也没有人带着编号搜词;还有 1.8 这种出现等于号“=”的,甚至出现一个不知道什么符号,类似叉号的“”(U+2718)我见都没见过的字符。

  1. 数据重复性依然非常高,尽管已经在 HTML 解析后的文本使用了字典进行词汇级别的集合运算,剩下的数据依然是有大面积重复的,这取决于数据的特性。比如上面说的 1.7 (a) hell on earth1,在最后一步得到的数据库中找到这个键,它有三个释义,竖着选中这三个释义,复制之,粘贴到 HTML在线解析 中查看:

可以发现有两条释义,一条以“hell-like”开头,另一条以“hell helling”开头,而第二条释义是第一条释义的子集,但是词头不一样,意味着分割后一个集合是 A{hell-like, ...},B{"hell helling", ...},所以这两个集合无论如何都不可能存在子集关系,所以它们都被保留。

这种重复我还没有想到什么符合逻辑的修改合并方式,更别提(让AI)给我编程实现了。

  1. 生僻字问题。 当打开最后一步所得的数据库后,前面的词条几乎是清一色的 HTML 字符表示法,如 &#x20ce8&#x235e5&#x241b6&#x24737&#x27422&#x27cac…这些生僻字是𠳨𣗥𤆶𤜷𧐢𧲬…这是原作者担忧有些生僻字无法在编修过程中有效处理,就用了代码表示,让词典软件在查询时实时渲染出来。这种思路自然是没问题的,这种生僻字在释义里面词典软件确实是可以实时渲染,但是反过来当词头那就根本没意义了,毕竟人是不会输入代码来查词的。
    这种是 HTML 代码的记录大约有 149 条(有重复),但是在释义中出现这种代码的就不知道了。一旦修改必须把所有用代码表示生僻字的部分都改为直接汉字显示,不然有可能造成跳转链接失效。
  2. 词典本身的错误。 具体参考:21世纪大英汉词典【20250323发现巨大问题,请立即停用所有同名词典】 我也是看到了这个帖子才发现我使用的版本有错误。还有其它错误可以在站内搜索。

7 求助与联系方式

还有现在一个问题就是,这个项目有没有前途呢?或者直白一些,把一个没有索引的较新的《21世纪大英汉词典》拿过来自己做一遍索引是否比在这个已经有词典改进更快、更省力?

又或者说不用做索引利用词典软件的“全文搜索”等功能会更好?

以上工作是我花了 5 个小时做的,如果想回头还来得及,毕竟没有付出多少时间成本。

综上所述,我需要各位大神的帮助,也需要各位论坛用户朋友的想法。当然如果能有大神愿意接手 fork 我这个项目那当然是感激不尽了。

联系我的方式:

  1. 此帖子评论,我会定期检查;
  2. 站内私信,我会定期检查;
  3. 邮箱 raterchan398087@gmail.com 或者 ivlivschan@gmail.com

整理这个浪费时间,全索引是用批量程序做的半成品屎山,选错研究对象就是白忙活。
再者辞典有错误这么多年了,也没有人有能力全部校对一遍,别折腾。
真的很喜欢,不如你去校对一份词语映射页码表都比这有意义。发现错误了还能根据页码找到原书图像。
我有词表206077词,谁有兴趣可以找我敲个页码。(每页敲首词和末词的页码)

彳亍,我不喜欢图像版的,图像版的有OCR也随便搜了,看着难受,切图还是难受

ENG: [Seeking Help] Prog. and Diff. for 21st CUECD

可以做到哪算哪,有所优化就好

有道理。这本词典很好用!。

不必一步到位,这样困难很大。还不如定一个阶段目标,比原来版本有进步就可以。然后,自然有人试用,帮着发现问题。