QMK 固件 Autocorrect(自动纠错)功能完全指南:原理、字典定制与回调扩展

发布时间:2026/9/14 6:54:01
QMK 固件 Autocorrect(自动纠错)功能完全指南:原理、字典定制与回调扩展 QMK 固件 Autocorrect自动纠错功能完全指南原理、字典定制与回调扩展【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware本篇技术指南以 QMK Firmware 仓库中的 Autocorrect 功能文档为主体结合 process_autocorrect.c、autocorrect_data.py 及 test_autocorrect.cpp 等源码与测试系统讲解该功能的触发原理反向 Trie 匹配、启用方式、自定义字典语法、qmk generate-autocorrect-data生成流程、防误触发策略以及三类用户回调函数process_autocorrect_user、apply_autocorrect、状态控制函数的完整用法。读完本文你可以在自己的键盘 keymap 中独立启用并深度定制自动纠错。功能概述在固件层面消灭习惯性错字日常输入中很多单词容易因肌肉记忆、按键顺序或手误而打错例如thier应为their、fitler应为filter、lenght应为length。QMK 的 Autocorrect 特性在固件内部维护一个最近按键的小缓冲区每次按键时检查缓冲区末尾是否命中字典中定义的错词typo一旦命中固件会自动发送退格键删除错误字符并补发正确的字符序列从而在键盘端直接修正错字减少输入错误的出现。该特性完全运行在键盘固件内不依赖操作系统或输入法的纠错能力因此适用于任何连接该键盘的主机。其默认字典位于 autocorrect_data_default.h共包含 70 条常见拼写错误的纠正条目如fales - false、becuase - because、guage - gauge等开箱即可体验。工作原理反向 Trie 高效匹配自动纠错的核心难点在于高效地检查缓冲区中是否存在错词既要控制内存占用也要保证查找速度快。文档与源码采用的方案是将全部错词表示为一棵Trie前缀树数据结构——树中每个节点是一个字母单词由通向叶子节点的路径构成。由于需要判断缓冲区是否以某个错词结尾Trie 以倒序写入查询时从缓冲区的最后一个字母开始依次向前匹配倒数第二个、倒数第三个字母……直到某个字母不匹配未命中或到达叶子节点命中错词触发纠正。以fitler为例其倒序 Trie 路径为r → e → l → t → i → f当用户敲入最后一个r时缓冲区恰好以r-e-l-t-i-f结尾随即触发纠正。在 C 实现中process_autocorrect维护typo_buffer与typo_buffer_size见 process_autocorrect.c缓冲区按AUTOCORRECT_MAX_LENGTH上限滚动满员时通过memmove丢弃最旧字符缓冲区长度小于AUTOCORRECT_MIN_LENGTH时直接返回最短错词之前的按键不可能命中。随后从缓冲区末尾向前用pgm_read_byte逐字节读取存储在 PROGMEM 中的autocorrect_data数组并匹配 Trie 节点命中叶子节点首字节最高位为 1即发现错词计算需要退格的次数并发送替换字符串。启用自动纠错在键盘或 keymap 目录的rules.mk中加入一行AUTOCORRECT_ENABLE yes默认情况下Autocorrect 处于关闭状态对应源码中keymap_config.autocorrect_enable的默认值。启用后还需在 keymap 中使用AC_TOGG键码打开它其开关状态会持久化保存在 EEPROM 中见autocorrect_enable()/autocorrect_disable()中对eeconfig_update_keymap的调用因此通常只需开启一次。状态控制键码键码别名说明QK_AUTOCORRECT_ONAC_ON开启 Autocorrect 功能QK_AUTOCORRECT_OFFAC_OFF关闭 Autocorrect 功能QK_AUTOCORRECT_TOGGLEAC_TOGG切换 Autocorrect 功能状态上述键码定义于 quantum/keycodes.hQK_AUTOCORRECT_ON 0x7C74并在 process_autocorrect.c 中被处理按下时分别调用autocorrect_enable()、autocorrect_disable()或autocorrect_toggle()并返回false阻止该键码继续传递。测试验证仓库自带单元测试 tests/autocorrect/test_autocorrect.cpp 可验证开关行为与纠错效果OnOffToggle测试逐一验证autocorrect_is_enabled()、disable()、enable()、toggle()的状态切换fales_to_false_autocorrection测试模拟依次按下F A L E S断言 HID 输出序列为F A L E BACKSPACE S E——即输入fales时固件自动发送一次退格并补上sefales_disabled_autocorrect测试在关闭状态下输入fales断言原样输出F A L E Sfalsify_should_not_autocorrect与overture_should_not_autocorrect测试验证fales/ture作为正确单词的子串时不会被误触发。这些测试精确印证了文档所述的行为命中错词即退格纠正关闭则透传且子串误触发需要通过词边界控制。自定义自动纠错字典字典文件语法创建一个文本文件每行一条typo - correction记录例如:thier - their fitler - filter lenght - length ouput - output widht - width语法要点行格式为typo - correctiontypo与correction之间的空白包括行首行尾会被忽略错词与纠正词不区分大小写生成器会把 typo 强制转为小写见parse_file_lines错词只允许字母a–z以及特殊字符:表示词边界纠正词可以是任意非 Unicode 字符以#开头的行与空行会被忽略。生成器 autocorrect_data.py 中的parse_file还会做额外校验重复的 typo 会被警告并跳过typo 不能互为子串否则较长的那条永远不会触发建议 typo 长度至少为 5 以避免误触发typo 超过 127 字符会报错退出。生成 C 头文件在仓库根目录执行将字典文件名替换为你的实际文件名qmk generate-autocorrect-data autocorrect_dictionary.txt该命令解析字典、构建倒序 Trie 并序列化为 C 数组最终在当前目录生成autocorrect_data.h。也可以指定键盘与 keymap让文件直接输出到对应 keymap 目录qmk generate-autocorrect-data -kb planck/rev6 -km jackhumbert autocorrect_dictionary.txt只要autocorrect_data.h位于你的 keymap 目录或 user 目录中编译时就会被自动拾取——process_autocorrect.c 通过__has_include(autocorrect_data.h)优先加载用户字典否则回退到默认字典并打印Autocorrect is using the default library.提示。生成的头文件形如// :thier - their // fitler - filter // lenght - length // ouput - output // widht - width #define AUTOCORRECT_MIN_LENGTH 5 // ouput #define AUTOCORRECT_MAX_LENGTH 6 // :thier #define DICTIONARY_SIZE 74 static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM {85, 7, 0, 23, 35, 0, 0, 8, 0, 76, 16, 0, 15, 25, 0, 0, 11, 23, 44, 0, 130, 101, 105, 114, 0, 23, 12, 9, 0, 131, 108, 116, 101, 114, 0, 75, 42, 0, 24, 64, 0, 0, 71, 49, 0, 10, 56, 0, 0, 12, 26, 0, 129, 116, 104, 0, 17, 8, 15, 0, 129, 116, 104, 0, 19, 24, 18, 0, 130, 116, 112, 117, 116, 0};其中AUTOCORRECT_MIN_LENGTH/AUTOCORRECT_MAX_LENGTH由最短/最长 typo 推导而来DICTIONARY_SIZE是序列化数组的总字节数固件据此分配缓冲区并做越界保护process_autocorrect.c 中state DICTIONARY_SIZE即安全返回。避免误触发词边界:的使用默认情况下typo 会在单词内部被搜索这有利于修正maxFitlerOuput这类长标识符中的错误但副作用是当 typo 恰好是某个拼写正确单词的子串时会被误触发。例如若字典中有thier - their它会在wealthier、filthier等正确单词上错误触发。解决方案是在 typo 前后加上词边界:来约束匹配范围。:匹配空格、句点、逗号、下划线、数字以及大多数非字母字符。以下表总结thier在不同边界写法下的匹配行为文本thier:thierthier::thier:看到thier错词匹配匹配匹配匹配单词thiers匹配匹配不匹配不匹配单词wealthier匹配不匹配匹配不匹配:thier:最为严格仅当thier作为完整独立单词出现时才触发。qmk generate-autocorrect-data在生成时还会尽力检查typo 作为正确单词子串会误触发的情况它会将每个 typo 与english_wordsPython 包中的约 2.5 万个英文单词比对未安装该包时可运行python3 -m pip install english_words安装。需要注意目前该检查仅覆盖英文单词。对应的警告逻辑位于parse_file与check_typo_against_dictionary例如不带边界的 typo 命中某个正确单词时会提示 would falsely trigger on correctly spelled word而完整成词:typo:且本身是正确单词时也会收到警告。未安装english_words时脚本会退化为一份极小的内建单词表含wealthier、loosest等作为兜底。覆盖自动纠错临时输入错词偶尔你确实需要原样输入一个错词例如正在编辑autocorrect_dict.txt时可以通过以下方式避免被纠正先输入该错词的前半部分在输入最后一个字母之前按下并松开 Ctrl 或 Alt 键再输入剩余字母。其原理是自动纠错实现不解析热键只要检测到除 Shift 以外的修饰键被按住就会清空 typo 缓冲区并重置自身状态见 process_autocorrect.cif ((*mods ~MOD_MASK_SHIFT) ! 0)时重置缓冲区并跳过处理。此外也可以用AC_TOGG键码直接切换 Autocorrect 的开关。用户回调函数深度定制process_autocorrect_user输入净化与异常处理原型bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods)该回调允许在按键进入纠错引擎之前对其进行自定义处理即净化输入。净化是必需的因为自动纠错只对 8 位的基础键码基础键码说明做 typo 字母匹配如果带有修饰键的键码或 16 位键码如 Shift A被直接传入字母检测会失败。例如 Mod-Tap 键LCTL_T(KC_A)是 16 位键码应被掩码还原为 8 位的KC_A。默认的process_autocorrect_user是一个weak弱定义函数其实现委托给process_autocorrect_default_handler见 process_autocorrect.c覆盖了 QMK 绝大多数特殊功能键码的使用场景包括本文前述用非 Shift 修饰键覆盖纠错的逻辑。用户可以在自己的keymap.c或其他代码文件中重新定义同名函数来覆盖它。自定义示例假设你有一个自定义键码QMKBEST应被当作单词的一部分忽略另一个自定义键码QMKLAYER应覆盖自动纠错可以在你的源码中扩展switch语句bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) { // 各匹配区间可参考 quantum_keycodes.h。 switch (*keycode) { // 排除以下键码不参与处理。 case KC_LSFT: case KC_RSFT: case KC_CAPS: case QK_TO ... QK_ONE_SHOT_LAYER_MAX: case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: return false; // 从带 Shift 的键码中提取基础键码。 case QK_LSFT ... QK_LSFT 255: case QK_RSFT ... QK_RSFT 255: if (*keycode QK_LSFT *keycode (QK_LSFT 255)) { *mods | MOD_LSFT; } else { *mods | MOD_RSFT; } *keycode 0xFF; // 取出基础键码。 return true; #ifndef NO_ACTION_TAPPING // 按住时排除 tap-hold 键轻点时提取基础键码。 case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: # ifdef NO_ACTION_LAYER // 层功能被禁用但 action tapping 仍启用时排除 Layer Tap。 return false; # endif case QK_MOD_TAP ... QK_MOD_TAP_MAX: // 非 Shift 修饰被按住时排除按住状态。 if (!record-tap.count) { return false; } *keycode 0xFF; break; #else case QK_MOD_TAP ... QK_MOD_TAP_MAX: case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // 相关功能被禁用时排除。 return false; #endif // 按住时排除换手swap hands键轻点时提取基础键码。 case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: #ifdef SWAP_HANDS_ENABLE if (*keycode 0x56F0 || !record-tap.count) { return false; } *keycode 0xFF; break; #else return false; #endif // 处理自定义键码 case QMKBEST: return false; case QMKLAYER: *typo_buffer_size 0; return false; } // 非 Shift 修饰键激活时禁用自动纠错。 if ((*mods ~MOD_MASK_SHIFT) ! 0) { *typo_buffer_size 0; return false; } return true; }注意在该回调中return false表示跳过该键码的自动纠错处理同时设置*typo_buffer_size 0会一并清空纠错缓冲区取消已存储的字母。默认处理器process_autocorrect_default_handler可在用户回调中调用以复用内置的键码区间处理逻辑。apply_autocorrect接管或扩展纠正动作原型bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct)该回调在错词被命中后调用传入需要退格删除的字符数backspaces、替换字符串str部分单词而非完整单词、完整的错词与纠正词字符串typo与correct。用户可以在此增加额外处理或完全替换默认的纠正动作。由于实现本身没有单词概念只有字母流传入的typo/correct只是最佳推断可能并不精确——例如可能得到wordtpyo与wordtypo而不是预期的tpyo与typo。示例一命中错词时播放提示音并自行执行纠正return false停止默认处理需手动退格并发送新字符#ifdef AUDIO_ENABLE float autocorrect_song[][2] SONG(TERMINAL_SOUND); #endif bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { #ifdef AUDIO_ENABLE PLAY_SONG(autocorrect_song); #endif for (uint8_t i 0; i backspaces; i) { tap_code(KC_BSPC); } send_string_P(str); return false; }重要str指向 PROGMEM 中的数据。若你return false并希望发送该字符串必须使用send_string_P而非send_string或SEND_STRING。示例二只检测并展示事件仍由内部代码执行纠正return truebool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { #ifdef OLED_ENABLE oled_write_P(PSTR(Auto-corrected), false); #endif #ifdef CONSOLE_ENABLE printf(%s was corrected to %s\n, typo, correct); #endif return true; }默认的apply_autocorrect为weak定义且直接返回true见 process_autocorrect.c此时 process_autocorrect.c 会执行默认纠正按backspaces次数发送KC_BSPC再调用send_string_P(changes)发送替换文本。纠正后若触发键是空格缓冲区重置为仅含空格以便继续匹配下一单词否则完全清空。状态控制回调函数以下函数可用于在自定义代码中编程控制 Autocorrect 状态声明见 process_autocorrect.h函数说明autocorrect_enable()开启 Autocorrectautocorrect_disable()关闭 Autocorrectautocorrect_toggle()切换 Autocorrect 状态autocorrect_is_enabled()返回 Autocorrect 当前是否开启附录Trie 二进制数据格式以下内容说明autocorrect_data中 Trie 的字节序列化方式。日常使用自动纠错无需关心这些细节此处仅为有兴趣修改实现或了解原理的读者记录。所有纠错数据存储在一个扁平数组autocorrect_data中每个 Trie 节点关联一个字节偏移根节点为 0。节点首字节的最高两位决定节点类型00⇒链节点chain node只有一个子节点的 Trie 节点01⇒分支节点branching node有多个子节点的 Trie 节点10⇒叶子节点leaf node对应一个错词并保存其纠正数据。分支节点每个分支用1 字节键码KC_A–KC_Z 指向子节点的 16 位小端字节偏移编码。所有分支依次序列化以零字节结尾节点类型通过将首字节与 64 按位或keycode | 64标记为分支。示例根节点的序列化示意------------------------------------------------- | R|64 | node 2 | T | node 3 | 0 | -------------------------------------------------链节点Trie 中常有很长的单子节点链如fitler中的 f-i-t-l。若按分支节点格式编码每个节点都要附带 16 位链接非常浪费空间因此链采用从最靠近根节点的字母开始的一串键码 零字节结尾的紧凑格式链尾子节点紧随其后编码可为分支节点或叶子节点。fitler中的 f-i-t-l 链编码为----------------------------------- | L | T | I | F | 0 | -----------------------------------链的中间位置也可被引用并按同样方式解码例如从i而非l开始子链格式一致。叶子节点对应某个错词并保存纠正数据。首字节为需要退格的次数随后是空字符结尾的替换文本 ASCII 字符串。触发后先按次数退格再把该字符串交给send_string_P。以fitler为例需要退格 3 次不是 4 次——最后一个r按下时即捕获错词并以lter替换叶子类型通过将退格次数与 128 按位或backspaces | 128标记------------------------------------------ | 3|128 | l | t | e | r | 0 | ------------------------------------------解码逻辑一个 16 位变量state表示当前 Trie 位置初始为 0 指向根节点。对每个键码测试state处字节的最高两位判断节点类型00⇒ 链节点若节点字节与键码匹配state加一前进到下一字节若下一字节为零再加一跳到后续节点01⇒ 分支节点在分支中查找匹配键码沿其节点链接前进10⇒ 叶子节点发现错词读取首字节得到退格次数将后续字节交给send_string_P发送纠正文本。上述编码/解码逻辑分别对应生成端serialize_trie与运行端process_autocorrect中code 64分支、code 128叶子的判断节点链接使用 16 位偏移因此整个字典的序列化数据上限为 64KBencode_link中超过0xffff会报错提示精简字典。结语Autocorrect 是 QMK 中一个小而精的特性反向 Trie 让错词匹配在常量级内存与线性时间内完成:词边界机制有效规避了子串误触发而三个层次的用户回调输入净化、纠正动作、状态控制使其可被深度定制。无论是直接启用内置字典、编写自定义纠错表还是像 test_autocorrect.cpp 中那样精确验证每次按键的 HID 输出本文介绍的配置与源码线索都能帮助你在自己的键盘上落地一套可靠、可预期的自动纠错方案。【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考