尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python解析Unihan自动生成双拼辅码表
折腾双拼辅码这件事我断断续续弄了快两年。最早是被自然码的音形结合惯坏了——打「张」的时候不再需要在候选框里翻三页找那个 zh 开头的字一个辅码字母下去候选框基本就剩两三个。这种手感一旦沾上再回去用纯拼音就像开惯了手动挡突然换回自动挡总觉得少点什么。可问题也来了换一台机器、重装一次系统、换个客户端版本那套辅码表就得重新找、重新导网上流传的版本又是五花八门收字范围不一样部首约定不一样格式还各有各的写法导进去不是缺字就是错位。后来我索性不再找现成的了自己写了个 Python 脚本从字符数据库里把每个字的部首提出来按自然码辅码的规则映射成字母直接生成一份手心输入法能吃的辅码表。整个过程一条命令跑完几十秒出结果收字范围自己定部首约定自己控哪天想改规则改一行 CSV 重跑就行。这篇就把这套东西从头到尾拆一遍辅码到底是什么、原料从哪来、脚本怎么写、跑起来会遇到哪些坑。适合两类人看——一类是被辅码表折腾过的自然码用户另一类是手上有输入法配置需求、想拿这个当模板改改就用的朋友。哪怕你完全没写过 Python照着抄也能跑通。1. 先想清楚辅码表这件事到底难在哪1.1 自然码辅码的真实作用机制自然码这类音形结合方案本质是在拼音码后面再挂一位「形」的信息。你打一个字拼音部分负责把候选范围收缩到几十个辅码再把范围压到个位数。举个例子「机」和「基」拼音都是 ji同音字一大把但「机」是木旁、「基」是土旁形码一区分就干净了。自然码的辅码取法主流做法是取字的部首然后按部首的读音首字母定键位。木取 m土取 t水取 s金取 j。听起来很直白但真正的规则细节远比这个复杂偏旁变体怎么归并氵和水的区别、康熙部首和日常理解的部首对不上怎么办「胖」在字典里归肉部但用户心里想的是月旁、有些部首根本没有对应的声母比如「冂」「匚」又该落到哪个键。这些细节在不同版本的自然码方案里约定不完全一样这也是为什么网上流传的辅码表彼此差异那么大。辅码带来的收益是实打实的。纯拼音打单字重码率高得离谱加了辅码之后常用字的平均候选数能降到两三个以内而且是可以形成肌肉记忆的——手指知道这个字该补哪个字母不需要看屏幕。代价是你得记住每个字的部首归属这个学习曲线前一两周很陡之后就平了。1.2 现成码表为什么总差那么一口气我试过至少五份来源不同的辅码表没有一份能直接用痛快。问题集中在三块。第一块是收字范围。有的表只收了两千多常用字打个稍微偏一点的字就退回纯拼音有的表一股脑塞了七万多个字结果文件大得加载卡顿还混进一堆异体和生僻编码。真正舒服的区间是 GB2312 加 GBK 的常用部分六千到一万字左右。第二块是部首约定不一致。同样一个「腾」字有的表按月部给 y有的按马部给 m。这不是谁对谁错的问题是方案版本差异但混着用就一定会精神分裂。第三块是格式。有的是「字空格辅码」有的是「字制表符辅码」有的是「辅码字」还有的带全码列。导入失败最常见的原因根本不是你码表内容错了就是分隔符和列顺序没对上。想明白这三点之后我的结论很简单这东西必须自己能生成规则掌握在自己手里出问题改一行重跑比在一个黑盒文件里大海捞针强一百倍。1.3 整体思路把「找部首」和「定字母」拆成两件事整套脚本的设计思路是把问题切成泾渭分明的两层。下层是数据层从字符数据库里拿到每个汉字对应的部首序号。这个活不用自己干国际字符数据库 Unihan 里有个字段叫 kRSUnicode格式是「部首序号.剩余笔画数」覆盖了 CJK 基本区和扩展区的大部分汉字。这比自己去写拆字规则稳得多——自己拆字你得处理上下结构、左右结构、包围结构写到最后会发现自己是在重造轮子。上层是映射层把部首序号翻译成辅码字母。这一层不能自动化因为它是人的约定。我的做法是单独维护一个 CSV 文件两列部首序号、对应字母。脚本只负责查表从不硬编码规则。这样哪天你想换一套部首约定改 CSV 就行代码一个字都不用动。两层之间再塞一个「修正层」专门处理那些数据库归部和用户直觉不一致的字比如「胖」「初」「腾」这类。这个修正表用字典写在脚本里几十行的事但能干掉绝大部分「打出来不对」的抱怨。2. 原料准备三张表决定最终质量2.1 汉字部首数据从哪来怎么选首选就是 Unihan 数据库里的 Unihan_IRGSources.txt。这个文件是纯文本几兆大小每行三个字段用制表符隔开字符编码、字段名、字段值。我们要的只有字段名为 kRSUnicode 的行。它长这样U4E00 kRSUnicode 1.0 U5B66 kRSUnicode 39.5 U80D6 kRSUnicode 130.5第一列是 U 开头的十六进制码位第三列就是答案。这里有个细节要记住值可能是「130.5」这种标准形式也可能带撇号表示简化部首比如「9.2」还可能一行里有多个值用空格隔开表示这个字在不同字典体系下归部不同。解析的时候取第一个、去掉撇号、按点号切分取整数部分就得到部首序号。为什么不用别的数据源有些在线字典接口能给部首但要联网、有频率限制、结果还得清洗。Unihan 是离线文件一次下载永久可用字段规范没有解析歧义。唯一的短板是它按康熙 214 部首归部跟日常理解的偏旁有偏差这个后面用修正层补。另外建议顺手把 Unihan_DictionaryLikeData.txt 也下下来里面有个 kTotalStrokes 字段笔画数信息在兜底逻辑里用得上——万一某个字缺部首数据还能按笔画数给个默认码总比整条丢掉强。2.2 部首序号到辅码字母的映射表这张表是整个项目的灵魂也是最需要你亲自核对的部分。我的建议是先按部首的原型字读音大胆铺一遍跑出结果后拿几十个常用字人工抽检错的再回头改。下面是起步用的示例映射覆盖了高频部首注意这是我按常见约定整理的示意版本你手上那套方案如果约定不同直接改 CSV 里的字母就行部首序号部首辅码说明9人亻r变形自动合并18刀刂d19力l30口k高频部首32土t38女n39子z46山s50巾j53广g57弓g61心忄x64手扌s按「手」的声母66攴攵w按「文」取键72日r74月y但肉部需修正见 2.475木m85水氵s86火灬h94犬犭q96玉王y102田t109目m112石s113示礻s115禾h118竹⺮z120糸纟s按「丝」取键128耳e130肉月r关键分歧点140艸艹c142虫c149言讠y154貝贝b157足z159車车c167金钅j170阜阝左f173雨y184食饣s187馬马m196鳥鸟n这里有个特别省心的地方康熙部首是按「原型」归类的氵永远记在水部85底下扌永远记在手部64底下忄永远记在心部61底下。也就是说变体归并这个最容易出错的环节数据库已经替你做完了。你要做的只是决定水部对应 s 还是别的字母。这一点让整个方案的复杂度直接降了一个量级。CSV 文件建议这么写用 UTF-8 带 BOM 保存方便用 Excel 直接打开编辑radical_no,radical_char,fuma,note 9,人,r,亻同部 30,口,k, 61,心,x,忄同部 64,手,s,扌同部 85,水,s,氵同部 130,肉,r,与月部需人工区分2.3 目标格式以本地模板为准各个客户端对辅码表的格式要求不完全一样我不建议你去猜。正确做法是先在你自己的软件里找到「自定义辅助码」或者「导入辅码表」的入口看看有没有「导出」按钮导出一份空模板或者示例用文本编辑器打开看清楚三点列的顺序字在前还是码在前、分隔符空格还是制表符、文件编码。我见过的情况里制表符分隔、字在前、UTF-8 无 BOM 是最常见的组合。但这不是标准只是经验之谈。所以脚本里我把输出这一步单独抽成一个函数改一行就能切换def format_line(char, code): # 制表符分隔字在前码在后 return f{char}\t{code}要换成空格return f{char} {code}要换成码在前return f{code}\t{char}。就这么简单不用动其他任何逻辑。2.4 修正表处理数据库归部和用户直觉的错位这是整套方案里最能体现经验的部分。Unihan 严格按康熙字典归部但用户打字的直觉不是字典学的。举三个最典型的例子。「胖」字在 Unihan 里归肉部130辅码按上表会得到 r。但普通人看到「胖」第一反应是月旁期望的辅码是 y。这个字不改用起来就是别扭的。「初」字归刀部18因为字形从衣从刀。但用户直觉是衣旁期望 y。按表跑出来是 d错了。「腾」字归马部187跑出来 m没问题但有些人习惯按「月」取 y这就属于方案版本差异了看你跟哪一派。处理办法是在脚本里维护一个覆盖字典优先级高于部首查表结果OVERRIDE { 胖: y, # 康熙归肉部按用户直觉取月 初: y, # 康熙归刀部按用户直觉取衣 腾: m, # 明确锁定避免方案摇摆 }这个字典怎么积累跑完第一版之后拿你自己最常用的两三百字挨个打一遍打不对的记下来往里加。别想着一次到位我第一版只有十几个条目用了半年才补到六十多条都是真打错的字。这个过程听着原始但效率最高——因为你只会为真正高频的字付出成本。3. 脚本核心实现三个函数搞定3.1 数据解析与边界情况处理解析 Unihan 的函数不长但要处理几种边界。空行和注释行要跳过文件里以 # 开头的行不少字段数不足三个的行要跳过非 kRSUnicode 的字段直接忽略。码位转换用int(code[2:], 16)因为第一列固定是「U」开头加四位以上十六进制。import csv from pathlib import Path UNIHAN_PATH Path(Unihan_IRGSources.txt) RADICAL_MAP_PATH Path(radical_map.csv) OUTPUT_PATH Path(fuma_output.txt) def parse_unihan(path): result {} with path.open(encodingutf-8) as f: for line in f: if not line.strip() or line.startswith(#): continue parts line.rstrip(\n).split(\t) if len(parts) 3 or parts[1] ! kRSUnicode: continue try: char chr(int(parts[0][2:], 16)) except ValueError: continue result[char] parts[2] return result def pick_radical(value): first value.split()[0] radical_part first.split(.)[0] radical_part radical_part.rstrip() try: return int(radical_part) except ValueError: return Nonepick_radical里那两行拆分值得多说一句。value.split()[0]是在处理多值情况比如某字同时归两个部取第一个rstrip()是在处理简化部首标记比如「9」要还原成 9。这两个操作顺序不能反否则撇号会挡在点号前面导致切分失败。3.2 查表、兜底与覆盖率控制生成主函数的结构是这样先加载映射表再遍历所有字每个字先查覆盖字典命中就直接用没命中就查部首映射还没命中就记进「未映射清单」并跳过。跑完之后打印统计告诉你缺了多少、缺的是哪些部首序号。def load_radical_map(path): mapping {} with path.open(encodingutf-8-sig, newline) as f: for row in csv.DictReader(f): try: no int(row[radical_no]) except (ValueError, KeyError): continue code (row.get(fuma) or ).strip().lower() if len(code) 1 and code.isalpha(): mapping[no] code return mapping这里encodingutf-8-sig是个细节专门对付 Excel 保存 CSV 时偷偷加的 BOM。不加这个参数第一列的表头会读成\ufeffradical_no然后整个表加载失败而且报错信息完全不提示原因非常恶心。主流程def main(): radical_map load_radical_map(RADICAL_MAP_PATH) char_data parse_unihan(UNIHAN_PATH) rows, missing [], {} for char, value in char_data.items(): if char in OVERRIDE: rows.append((char, OVERRIDE[char])) continue no pick_radical(value) if no is None: continue code radical_map.get(no) if code is None: missing[no] missing.get(no, 0) 1 continue rows.append((char, code)) rows.sort(keylambda x: x[0]) with OUTPUT_PATH.open(w, encodingutf-8, newline\n) as f: for char, code in rows: f.write(format_line(char, code) \n) print(f生成 {len(rows)} 条) print(f未映射部首 {len(missing)} 个共 {sum(missing.values())} 字) for no, cnt in sorted(missing.items(), keylambda x: -x[1])[:10]: print(f 部首 {no}: {cnt} 字)输出那行有个小机关newline\n。Windows 下 Python 默认会把\n转成\r\n大部分客户端能认但少数严格解析器会把\r当成内容的一部分导致最后一条记录吃不到。显式指定\n就绕开了这个不确定性。3.3 收字范围怎么卡直接跑全量 Unihan 会得到七八万条记录对日常使用毫无必要还会拖慢加载。我的做法是加一道过滤只保留 CJK 基本区加扩展 A 区的字也就是码位在0x4E00到0x3400之间……这里说反了正确范围是基本区0x4E00–0x9FFF、扩展 A 区0x3400–0x4DBF。这两段加起来两万多字覆盖了几乎所有实际会打的字。def in_range(char): cp ord(char) return 0x4E00 cp 0x9FFF or 0x3400 cp 0x4DBF如果你想要更克制一点只收 GB2312 那 6763 个一级二级字那就再叠一道判断用char.encode(gb2312)能不能成功来筛。这个写法很土但极其有效def is_gb2312(char): try: char.encode(gb2312) return True except UnicodeEncodeError: return False两种策略各有取舍。全量两万字的好处是打生僻字不用退回去重打坏处是文件大、某些客户端加载时会有肉眼可见的延迟。六千字的好处是轻坏处是遇到人名地名里的异体字就抓瞎。我个人用的是两万字方案因为实际用下来那个加载延迟不到一秒能接受。4. 实操全过程从下载到导入4.1 环境与文件准备Python 版本用 3.8 以上就行脚本只用了标准库 csv 和 pathlib不需要 pip 装任何东西。这一点挺重要——很多人的机器上装着各种环境装包容易出冲突纯标准库的东西拷过去就能跑。目录结构我习惯这么放一眼能看清谁是数据谁是代码fuma/ ├── gen_fuma.py ├── radical_map.csv ├── Unihan_IRGSources.txt └── output/ └── fuma_output.txtUnihan 文件解压后如果后缀是 .txt 直接改名或改路径就行脚本不关心它叫啥。第一次跑之前先确认三件事CSV 的列名是不是radical_no/fuma、文件编码是不是 UTF-8、有没有空行夹在中间空行不会报错但会静默跳过容易让人以为漏了数据。4.2 跑第一版并抽检命令行进目录python gen_fuma.py正常情况下两秒内出结果。屏幕上会打三行生成条数、未映射部首个数、最缺的前十个部首序号。第一版的结果百分百不完美别急着导入。打开输出文件随机抽二十个字人工核对。我第一版抽检发现的问题是部首 74月和 130肉因为我在 CSV 里都给了字母导致「肌」「肝」这类字按肉部得到了 r而用户期望可能不同还有部首 66攵表里漏了所有带攵的字全被归进未映射。这就是为什么一定要先看未映射清单——它会直接告诉你表里缺了哪几块。补完映射表重跑再看一次统计。这时候有个很好用的自检手段统计每个辅码字母被分配了多少字跟汉字部首的实际频率分布对一下。辅码字母期望占比异常信号s水/手/糸/示/食最高多个大部合并如果偏低检查 85/64/120 是否漏配m木/目/马次高k口高j金/巾中等明显偏低看 167 有没有配x心中等冷门字母极低占比突增说明映射写错位了我第二次跑就靠这个发现了问题辅码 y 的占比异常高占了将近两成。排查下来是部首 74月、96玉、145衣、149言、173雨、196鸟全给成了 y其中 196 应该是 n。改完 y 的占比回落到合理区间。这种分布校验比逐字核对效率高得多强烈建议做。4.3 导入客户端并验证生效导入前一定先备份现有配置。这不是客套话辅码表加载失败有时候会导致整个输入方案回退之前调好的词库和快捷设置跟着丢非常闹心。导入流程大致是打开设置里的「输入方案」或「高级设置」找到辅助码相关项选择本地文件确认。有的版本要求先勾选「启用自定义辅助码」再导入顺序错了没反应这点看界面提示就行。验证方法很直接打一个你确定的字比如输入ma加辅码键看候选是不是只剩木旁的字。如果候选没变化八成是辅码没启用而不是码表错了。这时候别急着改码表先去看设置界面那个开关。再补一个验证维度多打十几个不同部首的字比如机m、河s、妈n、铁j、晴r每个都能收窄候选基本就说明整张表加载正常了。如果只有部分生效那多半是文件里某些行的分隔符不统一——这种情况常见于手工用 Excel 编辑过又混着脚本追加内容。4.4 后续维护改规则就是改 CSV这套东西最大的价值不在第一次生成而在于后续可维护。你用了两个月发现某些字总打不对就在 OVERRIDE 里加一条想换一套部首约定改 CSV 里的字母列想扩收字范围改那个码位判断函数。所有改动都不涉及算法逻辑改完重跑几十秒重新导入。我给自己的脚本加了个易用的小功能跑完之后自动把未映射的部首序号和对应的两个字样例打出来方便快速补表。实现很简单在 missing 字典里顺手存个样例字符missing[no] (missing.get(no, (0, ))[0] 1, char)这样打印出来直接是「部首 66攵: 128 字样例 收、改」比只看序号直观多了。5. 踩过的坑和对应解法5.1 问题速查表现象大概率原因处理方式导入报格式错误分隔符或列序不对用导出模板对比改 format_line只有第一行乱码文件带 BOM输出改用 utf-8 而非 utf-8-sig最后一条记录丢失换行符是 CRLF输出显式指定 newline\n某些字完全打不出辅码该字不在码表里检查收字范围函数部分字辅码是错的数据库归部与直觉不符加进 OVERRIDE 字典加载明显变慢收字太多加 GB2312 过滤CSV 读不出数据Excel 加了 BOM 或空行用 utf-8-sig 读跳过空行辅码启用但候选不变设置里开关没勾先去设置界面确认5.2 几条用血换来的经验第一条别信任何人给你的辅码表包括我这篇里的映射示例。方案版本之间的差异是真实存在的同一个字在不同派系里辅码不同完全正常。你要做的是接受一个基线然后用自己高频使用的字去校准别为了几个字来回改方案。第二条UTF-8 和 UTF-8 with BOM 必须搞清楚。这两个东西在文本编辑器里长得一模一样导入后的表现天差地别。我的判断方法很简单用 Python 读第一行repr()打出来看开头有没有\ufeff。第三条别在第一次就把映射表写到完美。我见过有人花两天时间把 214 个部首全部核对了一遍结果跑出来八万字加载要三秒最后还是回到两万字的版本。范围的成本和映射的成本要分开评估先把流程跑通再逐步打磨。第四条把所有中间产物留下来。我现在的输出目录里存着unmapped.txt、stats.txt、sample.txt分别记录未映射清单、字母分布统计、随机抽检样本。这些文件当下看着没用但下次改规则的时候把它们和上一版 diff 一下改动影响一目了然。第五条记得留一份能回滚的旧配置。辅码表是输入链路里的关键环节一旦出问题直接影响日常打字。我每次导入新表之前都会把当前配置文件复制一份加日期后缀出问题一分钟内切回去不耽误事。最后分享一个我用了挺久的小技巧在脚本里加一个--dry-run参数只统计不写文件。改映射表的时候先跑 dry-run 看未映射数量和字母分布有没有变好确认没问题再实际输出。这个习惯让我少导入了十几次废表省下的时间够我把修正表又补了二十条。
RELATED

相关推荐

用Python脚本自动生成自然码双拼辅码表

用Python脚本自动生成自然码双拼辅码表

自然码、双拼、辅码表、Python 脚本、手心输入法,这几个词凑在一起的时候,说明你已经不满足于"能打字",而是想把选字这一步也压到极致。双拼让一个汉字平均两键落地,但同音字太多,"shi"这个音能翻…

📅 2026/10/29 17:02:59
生产级智能体沙箱架构:隔离内核选型与安全实践

生产级智能体沙箱架构:隔离内核选型与安全实践

1. 为什么生产环境的智能体必须"关进笼子"1.1 一次让人后背发凉的攻防演练复盘我去年在团队里主导过一个企业内部的智能体平台项目,一开始大家的思路很朴素:把大模型 API 接进来,给它配上搜索引擎、数据库查询、工单系统这几个工具…

📅 2026/10/29 17:02:59
HEMT射频电路设计实战:从选型、偏置到匹配调试的避坑指南

HEMT射频电路设计实战:从选型、偏置到匹配调试的避坑指南

射频电路设计里,晶体管选型这件事,很多时候决定了整个链路的上限。我见过不少项目,前级低噪放指标怎么调都差那么一截,最后发现问题不在匹配网络,而是器件本身在目标频段的噪声特性和增益能力就摆在那儿。高电子迁移率…

📅 2026/10/29 16:57:58
MORE NEWS

更多资讯

📰

操作系统复习地图:五章核心考点与易错点全梳理

简介:这份资源是《计算机操作系统(第三版)》课后习题的参考答案文档,面向高校计算机专业学生、考研复习者以及准备操作系统课程考核的学习者,用于对照教材章节核对习题解答、梳理知识脉络。内容覆盖操作系统引论、进程…

📰

Oracle ADG备库路径转换故障分析与恢复方案

1. 问题背景与现象描述上周五凌晨,我们核心数据库的ADG备库突然报出"数据文件路径转换失败"错误,导致增量恢复同步过程中断。这个故障直接影响了业务系统的报表生成和数据分析功能。作为DBA团队负责人,我立即组织人员介入排查。具体…

📰

MARIS系统:几何增强与语义对齐的海洋实例分割技术

1. 项目背景与核心价值 海洋环境监测一直是计算机视觉领域极具挑战性的研究方向。传统实例分割方法在开放水域场景中面临三大痛点:水下图像质量差导致的几何特征模糊、海洋生物类别多样性带来的语义鸿沟、以及标注数据稀缺造成的模型泛化能力不足。MARIS系统正是针对…

📰

软件设计师中级考点精讲:数据结构、排序与计算机系统基础

简介:这份文档面向备考软件设计师(中级)的考生,聚焦软考中级核心考点,将数据结构、查找与排序等高频内容整理成便于速查的笔记精华,适合需要系统梳理知识框架、攻克难点公式与易错点的复习场景。压缩包共1个…

📰

营销技能树:从执行者到策略者的能力进阶地图

做营销最尴尬的一个瞬间,就是别人问你“你会什么”的时候,你只能说出“写文案、做活动、投广告”这种谁都会说的词。几年前我自己也卡在这个阶段,直到后来把个人能力拆成了一棵“marketingskills”技能树,才慢慢搞清楚&#xff1a…

📰

context-mode:SQLite+FTS5+BM25构建本地AI上下文检索模式

1. “context-mode”到底是什么?别被术语唬住,它本质是让AI真正“听懂上下文”的工程实践最近在多个技术社区和开发群聊里,“context-mode”这个词突然高频出现,尤其和MCP、SQLite、FTS5、BM25这些词绑在一起。很多人第一反应是&a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬