有没有和我一样的朋友:86 版五笔用了很多年,平时打字飞快,可偶尔碰到一个字就是打不出来。「鼎」到底怎么拆?「露」是 fkhk 还是别的?「凹」「凸」这种字,每次都要停下来,切到拼音,打出来,再反查一遍五笔编码。
反查的网站其实不少,但大多只给一个编码,最多再配一张字根图。我真正想知道的是为什么:这个字拆成了哪几个字根,每个字根对应字上的哪几笔,最后那个码是识别码还是字根本身。弄明白了,下次才不会再忘。
所以我做了一个小网站:五笔字根图解。

输入一个字,看它是怎么拆的
以「胜」为例。输入框里打一个「胜」,右边会列出它的四个码:
E:月T:撇G:青字头G:末笔字型识别码(末笔是横,左右结构,所以是 11 区位,也就是 G)
左边的田字格里,每个字根对应的笔画用同一种颜色标出来:月是红色,那一小撇是橙色,青字头是绿色。鼠标移到某个编码字母上,对应的笔画会单独高亮。
最下面列出这个字在微软五笔里的全部编码。比如「胜」有三级简码 etg 和全码 etgg,每一种都写明了怎么上屏;如果某个编码下它不是第一个候选,也会注明要按几。
这些说明文字不是我一个字一个字手写的,是根据拆分结果自动生成的。不同类型的字讲法不一样,比如成字字根「雨」:

「雨」本身就是一个字根,所以走的是"报户口 + 首笔 + 次笔 + 末笔"的规则:F + 横 G + 竖 H + 点 Y,也就是 fghy。
字根超过四个的字,只取第一、二、三和最后一个字根,中间不取码的笔画会显示成灰色。比如「鼎」(顺便一提,网站支持深色模式):

拆解演示
田字格下面有个「▶ 拆解演示」按钮,点一下会按顺序把每个字根依次点亮。这是「露」:

旁边还有个「笔顺」按钮,可以看整个字的书写顺序。
编码数据来自 Windows 自带的微软五笔
这是我最在意的一点:编码和候选顺序要和我平时用的输入法一模一样。
网上流传的 86 版码表有很多版本,简码、词组和候选顺序都不完全一致。查到一个编码,回到输入法里打出来的却是别的字,这种反查就没有意义了。
Windows 自带的微软五笔,词库就放在系统目录 C:\Windows\InputMethod\CHS\ChsWubiNew.lex 里。这个文件格式没有公开文档,好在结构不复杂:文件头之后是一条条变长记录,每条记录包含权重、最多 4 位编码和对应的字词,都是 UTF-16。解析的核心就这么几行:
js
// 记录格式:u16 长度 | u16 权重 | u16 编码长度 | 4×u16 编码 | UTF-16 词 | u16 0
let p = 0xa8
while (p + 2 <= b.length) {
const len = b.readUInt16LE(p)
if (len === 0) break
const weight = b.readUInt16LE(p + 2)
const codeLen = b.readUInt16LE(p + 4)
const code = b.toString('utf16le', p + 6, p + 6 + codeLen * 2)
const word = b.toString('utf16le', p + 14, p + len - 2)
lines.push(`${code}\t${word}\t${weight}`)
p += len
}
解析出来一共 53 万多条。同一个编码下,按权重从小到大排,就是输入法里的候选顺序。我拿几个常用编码在输入法里对过,顺序是一致的。
所以网站上看到的「第 2 候选,需按 2」,在你电脑上的微软五笔里也是按 2。
反过来:输入编码查字
输入框里直接打字母,就是按编码查字。比如输入 etg:

上面列出 etg 这个编码下的候选(顺序和输入法一致),中间是第一个候选字的完整拆解,下面是"继续输入":以 etg 开头的所有四码编码分别能打出什么。没事翻一翻,能顺手记住不少词组,比如 etgd 是"胜天",etgu 是"胜平"。
第 3、4 码也可以用 z 当万能键。
词组和整句
输入词组,会给出词库里的编码,以及这个编码是怎么从每个字里取出来的:

「中国人」是三字词,取前两个字的第一码、最后一个字的前两码:k l ww,合起来是 klww。不过 klww 的第一个候选是"别人",所以要打「中国人」得按 2。这一点网站上也标出来了。
如果输入的是一整句话,比如「今天吃什么」,词库里不可能有整句。以前我只会按规则推出一个 wgkt,但这个编码根本打不出这句话,会误导人。现在改成算出一个实际能用、而且最省键的分段打法:

- 推荐打法 :只用第一候选,按空格上屏(四码唯一时自动上屏),不用记任何选重数字。「今天吃什么」=
wygd␣ktn␣wftc␣,共 14 键。 - 优化打法 :允许按数字选重,键数更少。这里是
wygd␣kwtc4,共 10 键,但你得记住"吃什么"在kwtc下排第 4。
后台就是一个很普通的动态规划:把句子切成词库里存在的词和字,每一段的代价是"编码位数 + 上屏键",求总键数最少的切法。推荐打法和优化打法只是"是否允许选重"这一个开关的区别:
ts
export function commitKey(code: string, rank: number, n: number): string | null {
if (rank > 9) return null // 需要翻页的候选不考虑
if (code.length === 4 && n === 1) return '' // 四码唯一,自动上屏
return rank === 1 ? ' ' : String(rank) // 空格,或数字选重
}
字根表和口诀
「字根表」页是一张按键盘排列的字根图,横、竖、撇、点、折五个区用不同颜色区分,点击任意一个键可以看到它上面的全部字根:

下面附了那首经典的字根口诀,"王旁青头戋(兼)五一,土士二干十寸雨......",按区位排好,点一句就能看对应键的字根:

查询记录和复习
查过的字会自动记在浏览器本地(localStorage),不上传任何地方。点右上角的「记录」可以看到历史,哪些字特别容易忘,可以点 ☆ 标成"易忘":

标记过的字会出现在「复习」页,做成卡片的形式:先看字,自己在心里拆一遍,再点开对答案:

记录支持导出、导入,换电脑或者换浏览器也能带走。
技术上的一点说明
网站是纯前端的(React + TypeScript + Vite),没有后端,部署在 Cloudflare Pages 上。
全部数据在构建时预先处理好,切成两千多个小 JSON 分片,按字的码点、编码前两位、词组首字来分。查一个字通常只需要请求一两个十几 KB 的文件,CDN 缓存之后基本是秒出。
真正花功夫的是把笔画和字根对上。
字根拆分数据参考了我之前的另一个项目 search-wubi,笔画的矢量数据来自开源的 Make Me a Hanzi。但这两份数据之间并没有"第几笔属于哪个字根"的对应关系,现成标注好的只有两千多个字。
我的做法是:先从这两千多个已知的字里,学出每个字根通常由哪几种笔画组成(横、竖、撇、点、折的序列),再对剩下的字用回溯搜索去匹配;严格匹配不上,就放宽成笔画顺序可以调整的匹配,最后再用编辑距离兜底。拿已知答案的那两千多个字做验证,大约 95% 能完全对上。目前一共给 9500 多个字做了田字格高亮,常用的 3500 字基本都覆盖了。
少数字是靠放宽匹配或兜底算法推断出来的,可能不太准,讲解文字里会提示"自动推断"。如果你发现哪个字拆得不对,欢迎到 GitHub 提 issue。
最后
网址:https://wubi.sdcb.ai,手机和电脑都能用,不用注册,没有广告。
项目以 AGPL-3.0 协议开源:https://github.com/sdcb/wubi86-viewer,觉得有用的话欢迎点个 Star。
感谢阅读到这里,如果感觉本文对您有帮助,请不吝评论 和点赞,这也是我持续创作的动力!
欢迎关注微信公众号 .NET骚操作,或者加入 .NET骚操作 微信交流群:

(如果微信群二维码过期,请加入 .NET骚操作交流QQ群 :495782587)