受够了某狗输入法。
弹窗广告铺天盖地,关都关不掉——关闭按钮小得跟蚂蚁似的,点歪了直接弹出一整个浏览器窗口。这还不算完,用着用着发现电脑上莫名其妙多了一堆根本没装过的软件:某狗桌面壁纸、某狗浏览器、某某垃圾清理工具……我保证不是我装的,我绝对没有点过任何"安装"按钮。某狗在后台一声不吭就给你塞进来,跟小偷没什么两样。
这种流氓行径必须谴责。一个输入法而已,凭什么监听你的键盘、统计你的词频、扫描你的硬盘、把你的输入习惯上传到它们服务器?凭什么弹广告、装全家桶?
于是就有了这个项目——一个极简的拼音输入法,开源,不需要联网,隐私完全可控。没有广告,没有后台,没有全家桶,更没有藏在角落里的"推荐安装"。核心代码一千行出头,无任何第三方库依赖,干净透明,每一行都看得见。
功能上也没含糊:支持全拼/简拼、模糊音(z/zh、c/ch、s/sh、n/l、f/h、en/eng、in/ing)、繁简体一键切换(2000+ 字符映射 + 词汇级消歧 + 两岸 IT 术语差异)、用户自定义词库、词频自动学习、DP 拼音分割(输入 haiyoumeiyou 自动拆成「还有没有」)、多皮肤配色、候选词数量可调……该有的都有。
一句话:我的输入法,我做主。
- 零联网: 不联网、不上传、不收集隐私
- 零广告: 没有弹窗,没有推广,永远不会有
- 零捆绑: 不安装任何第三方软件
- 代码可审计: 开源,核心代码一千余行,每个人都能看
- 全拼 / 简拼: 支持完整拼音和首字母缩写(
nh→ 你好) - DP 拼音分割: 输入长串拼音自动拆成词组(
haiyoumeiyou→ 还有没有) - 内置词库: 基于 rime-ice 词典,直接加载原版 YAML 格式,46,000+ 单字,540,000+ 词组,480,000+ 简拼
- 可配置热键: 切换热键支持 Alt+Shift / Ctrl+Shift / Right Shift / Ctrl+Space
- 系统注册: 可注册为系统输入法,设置开机自启,集成到 Windows 语言栏
- 用户词库: 自学习,自动记录选词频率,越用越顺手
- 词频动态调整: 每次选择自动 +1 频率,下次优先显示
- 预设皮肤: 6 款配色(默认浅灰、简约白、深邃黑、护眼绿、暖米黄、清爽蓝)
- 自定义配色: 通过取色器自由选择主色调,自动生成整套配色
- 候选词数量: 5-9 个可调
- 字体大小: 12-36px 可调
- 横排 / 竖排: 候选框自由切换
- 微软雅黑: 设置界面统一使用微软雅黑字体
- 模糊音: 支持 7 组模糊音(z/zh、c/ch、s/sh、n/l、f/h、en/eng、in/ing)
- 繁简体转换: 2000+ 字符映射 + 180 词汇级消歧 + 40 两岸 IT 术语
- 一简对多繁智能消歧(如「发」→「發/髮」、「后」→「後/后」)
- 最长词匹配优先策略
- 智能拼音纠错: 自动识别常见拼写错误
- 中文标点: 自动转换
,。!?——「」 - 设置窗口: 纯 Win32 手写 UI,无资源文件依赖
- 用户词典管理: 可视化增删改查
- UIAutomation 光标检测: 精准定位文本输入光标,候选框绝不挡字
- PageUp / PageDown 翻页: 除了
-/=,也支持 PageUp/PageDown - ⚙ 设置按钮: 候选框上可直接点击齿轮图标打开设置
- Windows 8+
- Visual Studio 2019+
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
cd build && PinyinIME.exe在 VS2012 Developer Command Prompt 中:
cd D:\codes\github\InputMethod
build.bat编译产出一个 PinyinIME.exe,静态链接 CRT,无需安装任何运行时,拷贝到哪跑到哪。
- 运行
PinyinIME.exe(需管理员权限) - 按 Ctrl+Shift(默认,可在设置中修改)切换中英文模式
- 中文模式下输入拼音:
- 按 空格 确认第一个候选
- 按 数字 1-9 选择对应候选
- 按 Enter 或 Shift 将拼音字母直接上屏(不选候选)
- 按 - / 或 PageUp / PageDown 翻页
- 按 Backspace 删除最后一个字母
- 按 Escape 清空输入
- 点击 ⚙ 图标打开设置
- 按 Ctrl+Shift 切回英文模式
💡 打开设置 → 点击「📌 注册输入法到系统」可将 PinyinIME 注册为系统输入法,并设为开机自启。
| 文件 | 行数 | 说明 |
|---|---|---|
main.cpp |
~1180 | 主程序:键盘钩子、拼音引擎、候选窗口、文本注入、UIA 光标检测、拼音分割 |
settings.h |
~1140 | 设置系统:设置窗口 UI、皮肤管理、用户词典对话框、系统注册 |
s2t_data.h |
~810 | 简繁转换:2000+ 字符映射 + 词汇消歧 + 两岸 IT 术语 |
dictionary.h |
~230 | 词库加载器:直接读取 rime-ice 原版 YAML 词库,支持字符→拼音自动注音、简拼生成 |
trie_dict.h |
~100 | 前缀树 (Trie) 内存数据库:26 分支固定数组,O(L+M) 前缀查找 |
trie_dict.cpp |
~170 | TrieDict 实现:插入、精确查找、前缀搜索(带深度限制)、遍历 |
CMakeLists.txt |
~85 | CMake 构建脚本 |
build.bat |
~40 | 一键编译脚本 |
rime-ice/ |
- | rime-ice 词库(git submodule),cn_dicts/ 目录在构建时自动复制到输出目录 |
user.dict |
- | 用户自学习词库(运行时自动生成) |
每个节点 216 字节,固定 26 子节点指针(a-z),无哈希、无碰撞:
┌───────────────────────────────────────────────┐
│ TrieNode (216 bytes) │
│ │
│ children[26]: TrieNode* 数组 (208 bytes) │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │...│ │
│ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │
│ ↓ ↓ ↓ ↓ ↓ ↓ │
│ ... ... ... ... ... ... │
│ │
│ entries: vector<Entry>* (8 bytes) │
│ 仅终端节点非空 ← 避免空 vector 浪费 │
└───────────────────────────────────────────────┘
索引: ch - 'a' 直接定位,一条 mov 指令完成,比 std::unordered_map 哈希快 3-5 倍。
[root]
/ | \ ← 26 分支
a b c ...
/ |
n ban ───── terminal ──── [办 956075] ← 已按词频降序
/ [半 663254]
g [版 432109]
/ [班 321098]
ang ── terminal [般 210987]
[昂 38921] ...
[肮 12340]
/
xiang ── terminal
[想 500000]
[向 450000] ← 向量中词条已按 freq 降序
[像 400000]
[象 350000]
/
xian ← 非终端节点 (entries=nullptr)
\
z ← 非终端节点
\
ai ── terminal ──── [现在 501033]
[先在 8234]
[贤才 1230]
\
ao ── terminal ──── [先造 500]
键空间: ~50k 拼音键 (xianzai, jintian, zhongguo...), 全部由小写字母 a-z 组成。
1. 沿树走到前缀节点 ───────────────── O(5) 步
root → x → i → a → n → z
↑
当前节点 (非终端)
2. DFS 收集子树所有终端词条 ───────── O(M) 节点
z ← depthLeft=6 (从子节点起计)
│
ai ── [现在 501033] ← 第1层
│ [先在 8234]
│ [贤才 1230]
│
ao ── [先造 500] ← 第1层
│
... (最多再搜 5 层深)
每终端取前 3 高频词条 → 合并到结果 map (word → max freq)
3. 结果按词频降序排列
现在 501033
先在 8234
贤才 1230
先造 500
...
输入长度 maxDepth 覆盖范围
────────────────────────────────────────
1 字母 6 全部单音节 (zhuang=6)
2 字母 5 "zh" → zhuang (再5层)
3+字母 0(无限) 前缀已具体, 子树很小
std::unordered_map TrieDict
─────────────────────────────────────────────────────
精确查找 O(1) 哈希 O(L) 指针追
前缀查找 O(N) 全表扫描 ⚠️ O(L+M) DFS
单次按键耗时 10-50ms (卡顿) 0.1-0.5ms
内存 (索引) ~30MB (bucket开销) ~43MB (节点)
L= key 长度 (≤6 单音节, ≤12 双音节)M= 子树内终端节点数 (通常 < 100)- 写入仅初始化时发生,运行时只读 → 无锁、无内存分配
┌─────────────────────────────────────────────────────────┐
│ WinMain (消息循环) │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ KeyboardHook │ │ CandidateWin │ │
│ │ WH_KEYBOARD_LL│ │ WS_POPUP │ │
│ │ + Caps/Shift │ │ + GDI 自绘 │ │
│ └──────┬───────┘ └──────▲───────┘ │
│ │ │ │
│ ┌──────▼─────────────────┴────────────────────┐ │
│ │ PinyinEngine (拼音引擎) │ │
│ │ ┌────────────────┐ ┌──────────────────┐ │ │
│ │ │ 候选词匹配 │ │ 拼音分割 (DP) │ │ │
│ │ │ + 精确匹配 │ │ + 410 有效音节 │ │ │
│ │ │ + 前缀联想 │ │ + 束搜索 top-8 │ │ │
│ │ │ + 大小写忽略 │ │ + 多策略组合 │ │ │
│ │ └───────┬────────┘ └──────────────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼────────────────────────────┐ │ │
│ │ │ TrieDict (前缀树内存数据库) │ │ │
│ │ │ · 26 分支固定数组,无哈希 │ │ │
│ │ │ · O(L+M) 前缀查找 │ │ │
│ │ │ · DFS 深度自适应限制 │ │ │
│ │ │ · ~50k 拼音键, ~540k 候选词条 │ │ │
│ │ └────────────────────────────────────┘ │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ 用户词库 + 模糊音 + 简繁转换 │ │ │
│ │ └────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ SettingsWindow │ │ IUIAutomation │ │
│ │ + 皮肤/配色 │ │ + TextPattern2 │ │
│ │ + 用户词典管理 │ │ + GetCaretRange │ │
│ │ + 系统注册 │ │ → 精准光标定位 │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────┘
基于《简化字总表》三表体系:
- 表一 (~350 字):不作简化偏旁的简化字
- 表二 (~132 字 + 14 简化偏旁):可作简化偏旁的简化字
- 表三 (类推简化,1500+ 字):应用表二偏旁类推简化
转换策略:最长词匹配优先 → 单字映射回退,确保「一简对多繁」正确消歧。
词汇覆盖:
- 一简对多繁词组消歧 ~180 对(如 出发→出發 / 头发→頭髮)
- 两岸 IT 词汇差异 ~40 对(如 软件→軟體、鼠标→滑鼠、服务器→伺服器)
每次选择候选时:
- 该候选在用户词库中频率 +1
- 自动保存到
user.dict文件 - 下次输入相同拼音时,高频词优先显示
- 候选排序 = 系统词库基础频率 + 用户学习频率
- 全局键盘钩子(
WH_KEYBOARD_LL)需以管理员权限运行,否则在某些应用中无法正常输入 - 候选框使用 GDI 纯手工绘制,无任何 UI 框架依赖,无任何第三方库依赖
- 首次运行会在同目录生成
pinyin_config.ini(配置文件)和user.dict(用户词库) - UIAutomation 光标检测在 Chrome/Edge/VSCode/Office 等现代应用中工作良好,老旧程序回退到 Win32/MSAA 检测
MIT License
