自制工业控制系统:中文输入法——音节切分、三层码表与候选窗
Contents
起因
汉字进机器,是一道比机床数控更老的难题。机械时代的中国打字机拖着一块两千多常用字的大字盘,拣字锤在盘面上跑,熟练工一分钟二十来字,比手写快不了多少;林语堂 1947 年的明快打字机想换思路——几十个键配一套机械索引机构,按上下形检字,从七千字里调字,样机造出来了,赔进去的是他半生的版税。更早的 1871 年,丹麦大北电报公司在上海敷设海缆,汉字被编成四位数字一字的电码本,收发两头的译电员"数字进、汉字出"——那份码本就是"码表"这个词的老祖宗:输入的从来不是字本身,是查表的键。
1980 年 GB2312 定了 6763 字的常用集,一级 3755 字干脆按拼音排序——标准自己就把"音"当索引。1983 年两件事同年发生:南阳的王永民拿出五笔字型,把汉字拆成一百多个字根拼形,重码率压到能盲打,此后二十年专业录入员的标配;北京电子部六所的 CCDOS 上线,PC 第一次能显示、能输入汉字,内置拼音、区位、首尾三种输入法。三十年后拼音赢了,不是算法赢了,是人群赢了——小学拼音是义务教育白送的底子,五笔得专门背字根,车间里没人肯为打几个工件名去练那个。
车间的现实一直很朴素:HF 的编程屏带拼音,AutoCut 也带;操作工要给工件起名、要写"淬火后割"这样的注释、登录要认自己的名字。这台机器不需要云端整句,需要的是 1983 年那一层的字/词级拼音,外加一本本地小账给车间热词升频。三线在这里合拢:按键流(键鼠篇)有了,16×16 点阵字库和 UTF-8 内码(国际化篇)有了,编程屏的文本框(界面七屏)在等着——中间缺的那层,就是把拉丁按键流变成汉字的输入法。仿真屏那条线刚收口(3D 模拟),这篇补上车间里最不起眼、却天天要用的这一层。
一、车间里到底要敲什么字
先数需求,再谈算法。全系统要敲汉字的地方一张手数得完:
- 编程屏:工件名(二十字符封顶)加一行注释
- 权限屏:操作工的名字
- 参数、维护屏:备注字段
没有长文本,没人会在这台机器上写邮件——它是机床不是工作站。这个收窄直接砍掉现代输入法最贵的部分(整句统计模型、云词库、无限联想),剩下三件硬活:
- 音节切分——一串拉丁字母切成合法音节
- 码表查询——音节查候选,按频率排序
- 一个状态机——组合、候选、上屏三态循环
边界之外的不做,但留缝:五笔、区位这类拼形/数字方案不实现,引擎做成可换的实现(第四节),跟卡适配层一个思路——拼音是这台机器的第一种输入法,不是唯一一种。还有个顺手的边界在别处已经定过:老 U 盘上的 GB2312 文件名,读的时候转一次 UTF-8(U 盘篇的口径),输入法不背这个锅。
二、拼音的底账:音节、击键与切分
普通话不带调的合法音节四百一十个出头——声母二十来个乘韵母几十个,组合一大半不合法(“jiong"在、“juang"不在),这个事实值钱:音节表是个只有 410 行的封闭集合,构建期从拼音方案生成、字典序排好,运行时二分查找,最长音节 zhuang 六个字符,整个表两三 KB。
声调不管:zhōng 和 zhòng 进同一个候选桶,多出来的重码用频率排序消化。账很直接——6763 字摊到 410 音节,平均一桶十六七个字,频率序下常用字都在前几名,翻页是例外不是常态。
击键账拿这篇的标题词算:“锥度试件"全拼 zhuidushijian 十三键加四次选字,十七击四个字,一秒多的活;词库养熟后"锥度"“试件"各自成词,七字母加两空格九击。车间高频词(中走丝、钼丝、规准、淬火)头一回全拼选字,第二回就在词候选里——这就是用户词频那本小账的用处,第三节说。
真正有点算法含量的是切分。一串字母不是天然按音节分好的:xian 可以是 xian(先),也可以是 xi+an(西安);fangan 是 fang+an(方案)还是 fan+gan(反感),字面上看不出。贪心最长匹配永远切不出"西安”——xian 一口吞掉,这条 CC时代就踩过的坑不能重踩。两条路配合:
- 分隔符:击键里打单引号强制切分,xi’an 就是两个音节,零歧义
- 全切分 DAG:不加分隔符时,把每个位置当一个节点,keys[i..j] 是合法音节就连一条边(最长看 6 字符),边权取该音节桶首字的频率(整数),从 0 到 n 走一条最大权路径——动态规划一遍,几十个节点,微秒级
|
|
非法段的处理故意做得笨:某个位置任何音节都出不去(拼错了),那段字符就原样留在组合串里显示,候选窗给空。用户看见哪里断了,改了再继续——不做纠错,纠错是整句引擎的活,这个边界第一节已经划掉了。
三、码表:三层表、一本账
数据三层,全是静态只读:
- 音节表:410 行 × 最长 6 字节,排序数组加二分,约 2.5KB
- 字级候选:按音节分桶的字列表,桶内频率降序。6763 字全量摊开约 300KB,字集对齐国际化篇的三级字库口径
- 词表:常用词五万条(两三字词为主),连键带词约 1.5MB
来源与许可是构建期的事:词频语料选 CC0/公共领域的开源词频表,构建脚本(在开发机上跑,不进机床)从语料生成二进制码表,产物进系统镜像——和 Noto CJK(SIL OFL)栅格化点阵同一条流水线,许可盘点进同一份 SBOM。运行时整表载入 Core1 堆(多核篇定的唯一合法堆用户),两 MB 量级,实时侧一颗粮不碰。
候选的构造是字/词双轨:切分完成后,先拿去分隔符的全拼串整串查词表(fangan 查中"方案”,词候选置顶),查不中再按第一个音节给字候选——选一个字上屏,剩余音节留在组合串里继续。词命中时一次上屏整个词,这就是"锥度试件"从十七击缩到九击的那一步。
用户词频是另一本活账。码表只读,热调整不碰它:单独一张 UserFreq 小表(热词的键哈希加增量计数),挂在参数区,500ms 双副本世代提交——参数篇的机制原样复用,断电重启车间热词还在。排序时用户增量叠加静态频率,“中走丝"这种词库里未必有的行业词,选过两回就爬到 zhongzousi 候选的头上。
四、引擎状态机与编程屏的合同
引擎对外的全部面目是一个函数:进一个按键,出一串指示。状态三态:Idle(无组合串)、Compose(组合串在编辑)、Candidate(候选窗开着——其实候选窗在 Compose 态就常驻,Candidate 只是翻页时的说法,实现里合一)。
|
|
几条合同先立好,都是被使用习惯倒逼出来的:
上屏是唯一出口。编程屏的文本框只认 Commit 和正文级 Backspace,组合串不进文本框、不进段表、不参与存储——起名到一半切去看仿真屏,回来组合串还在(引擎状态不销毁),但存盘的永远只有上屏了的字。解析器那边(G 代码篇)拿到的工件名因此永远是干净的 UTF-8,不用猜哪段是没定稿的拼音。
退格分两层。组合串非空时删组合串最后一个字符,空了才透传给编辑器删正文。新手最烦的就是退格把上好的字删掉——这条合同是 CCDOS 时代就立下的老规矩。
英数透传。Shift 切中英,英文模式下一切原样通过:工件名一半是数字编号(“法兰盘45”),注释里夹"45号钢”,切两次比造混排逻辑便宜得多。HF 时代就这么用,没人抱怨过。
引擎不画屏。Composition 和 Candidates 交给 UI 层去渲染,引擎不摸显存——两层架构在输入法这里的投影:引擎是纯逻辑,画不画、画在哪,七屏的 shell 说了算。
五、候选窗:反显与点阵账
候选窗一横条:九个候选,每项一个 16×16 全角字加 8 像素间隔,项前置 1–9 的半角序号,合计 240×20 像素上下——在任何一块能跑图形篇原语的屏上都是一小条。选中项反显:这个效果在 1bpp 点阵上是白捡的——字库每字 32 字节位图,画字就是把 1 写前景色、0 写背景色,反显不过是前景背景这一对调色板索引互换,零额外数据零额外字形。视频时代长大的工程师容易高估反显的成本,点阵时代它就是一对下标。
翻页候选超过九个时用减号/等号或翻页键,窗尾显示页码。渲染跑 Core1 协作层,60fps 里占不上一帧的零头——输入法整条链路没有一处实时约束,这是它跟插补器最大的不同:慢了最多操作工骂一句,不会撞限位。
六、第五个开发机可测模块
数一数家底:解析器、插补器、锥度展开器、直纹面生成器——四个纯函数模块都能在开发机上 cargo test。输入法引擎是第五个:key() 进 KeyEvent 流、出 ImeOut 流,两张表当只读参数,没有中断没有硬件,天然的纯函数。测试三件事:
- golden 击键序列:一百个常用工件词(锥度试件、法兰盘、定位板、淬火割一……)录下击键和选字的完整序列,改切分或排序逻辑后全量回归,一键数都不能变
- 歧义回归:xi’an 出"西安”、xian 出"先”,fangan 候选头名是"方案"——DAG 边权动过之后这三条最容易翻车
- 热词幂等:同一序列喂两遍,第二遍的候选顺序必须因用户词频而变、且再喂第三遍不再变——收敛性,跟插补器回退往返归零是同一类断言
总结
实战清单:需求收窄(工件名/注释/人名,砍整句砍云,字/词级拼音够用、拼形方案留接口);音节账(410 封闭集合、二分、声调进候选桶消化、平均桶十六七字);切分(分隔符强制 + 全切分 DAG 频率最大路径,贪心最长切不出西安的坑绕开,非法段笨处理不纠错);码表三层(音节 2.5KB/字级 300KB/词表 1.5MB,CC0 语料构建期生成,Core1 堆加载)+ 用户词频活账(参数区 500ms 双副本,热词两回登顶);引擎合同四条(上屏唯一出口、退格两层、英数透传、引擎不画屏);候选窗 240×20、1bpp 反显=调色板索引互换;第五个 host 可测模块(golden 击键、歧义回归、热词幂等)。
跑通标志:编程屏敲"锥度试件",首次全拼十七击内完成;退出重进再敲,热词在位九击完成;断电重启,用户词频不丢;英文模式连打数字编号,无一键被吞;切去看 3D 仿真屏再回来,组合串还在;解析器收到的工件名永远是干净 UTF-8。
下一篇顺路线图走MES 对接:Modbus-TCP 的底子已经有了,往上加一层极简 HTTP/XML 做 MTConnect agent;报警码 u32 对 Modbus u16 的映射约定,也该在那篇定稿了。
参考链接
- GB2312 — 1980 年的 6763 字常用集,一级字库按拼音排序
- CCDOS — 1983 年电子部六所,PC 上第一套汉字系统
- 五笔字型 — 王永民 1983,拼形输入的巅峰,专业录入员的标配
- 明快打字机 — 林语堂 1947,机械索引调字的输入法前身
- Noto Sans CJK — SIL OFL 许可,点阵字库的栅格化源
Author 软件开发大郭
LastMod 2026-09-11