导入与导出
词库、短语、方案包、整机备份的导入导出入口、文件格式与合并策略,含 WindDict / Rime / TSV 格式规范
清风的数据流转分成两类:文本类(词库、短语)直接导出为可读的 .wdict.yaml;聚合类(方案包、整机备份)打包为自描述的 .zip。这个分层决定了下面每一节的形态——文本类可以手工编辑、可以用 Git 管,聚合类则带清单、带校验、带合并策略。
总览
| 数据 | 导出格式 | 导入可接受 | 入口 |
|---|---|---|---|
| 方案词库(用户词 / 临时词 / 词频 / 候选调整) | WindDict .wdict.yaml(多段可选) | WindDict、Rime .dict.yaml、TSV 文本 | 设置 → 词库 |
| 快捷短语 | WindDict .wdict.yaml | WindDict | 设置 → 词库 → 快捷短语 |
| 方案包(方案 + 引用资源) | .zip(零层级) | .zip | 设置 → 方案 |
| 整机备份(全部用户数据) | .zip(带 manifest.toml) | .zip | 设置 → 高级 → 备份与还原 |
| 主题 | —— | .toml 文件 / HTTPS 链接 | 设置 → 外观 → 获取更多主题 |
三个入口互不通用
方案包与整机备份都是 .zip,但格式完全不同,各自的导入端都会识别并拒绝对方:备份页选到方案包会提示「该文件不是整机备份包」,方案页选到备份包会提示「该文件是整机备份包或旧格式归档」。
WindDict 文件格式(.wdict.yaml)
词库与短语的导出格式。一个文件 = YAML 头 + 若干 TSV 数据段,段与段之间用 --- !段名 分隔。
# WindInput 用户数据文件
wind_dict:
version: 1
generator: WindInput
exported_at: 2026-08-03T10:00:00+08:00
schema_id: wubi86
engine_type: codetable
sections:
words:
columns: [code, text, weight, count]
freq:
columns: [code, text, count, last_used]
--- !words
a 工 9999 42
ggll 五笔字型 500 0
--- !freq
def 有 7 1754186400头部字段
| 字段 | 必需 | 说明 |
|---|---|---|
wind_dict: | 是 | 格式标识。头部没有这一行就不认 |
version | 是 | 格式版本,当前只认 1;其它值直接报错 |
generator | 否 | 生成方,导出恒写 WindInput |
exported_at | 否 | 导出时间(RFC 3339) |
schema_id | 否 | 来源方案 ID,供导入端显示 |
engine_type | 否 | 来源引擎类型(codetable / pinyin / mixed),导入时用于跨类型校验,见下 |
sections | 否 | 各段的列声明;缺失则按该段的默认列解析 |
数据段与列
段标签写作 --- !段名,段内每行一条记录,字段用制表符分隔。文件可以只含其中几段,导入端只处理实际存在的段。
| 段标签 | 内容 | 默认列 |
|---|---|---|
words | 用户词库 | code, text, weight, count |
temp_words | 临时词库(自动造词的暂存区) | code, text, weight, count |
freq | 词频记录 | code, text, count, last_used |
shadow | 候选调整(置顶 / 隐藏) | action, code, word, position, cand_id |
phrases | 快捷短语 | code, text, weight, position, enabled |
各列语义:
weight—— 静态排序分;count—— 选词次数(调频热度),导入时取两边较大值合并last_used—— 最近使用时间,秒级时间戳action——pin(固定到position)或del(隐藏);del行的position/cand_id列留空cand_id—— 动态短语的稳定 ID,用于精准匹配同码同文的短语候选;无则留空enabled/ 布尔值一律写1/0
列顺序以头部声明为准
解析时按 sections 里该段声明的 columns 顺序取字段,不是按位置硬编码。因此老版本导出的三列 words 段(没有 count)仍能正确读入,缺失的列回退默认值。
转义规则
TSV 字段内若含以下字符,导出时转义、导入时还原:
| 原字符 | 写作 |
|---|---|
反斜杠 \ | \\ |
| 换行 | \n |
| 制表符 | \t |
不含这三者的字段原样输出,所以绝大多数行是纯明文,肉眼可读、可手改。
拼音编码带音节空格
拼音方案的 code 列写成带空格的音节码(ni hao 而非 nihao),与 Rime 源词库同形。落库时拆成扁平键 + 音节边界,列结构不变,因此老的无空格文件天然兼容——只是没有边界信息,消费端降级处理。
单音节词往返会丢失边界标记(ni 写出来还是 ni),这是刻意接受的损失:单音节没有切分歧义,且边界缺失在消费端一律是「放行」而非「拒绝」。
容错
- 文件可带 BOM,不影响解析
- 空行跳过;
words/temp_words/freq段字段数少于 2、shadow段少于 3、phrases段少于列数的行计入跳过数 shadow段action不是pin/del,或code/word为空的行跳过- 数字列解析失败回退
0,该行仍然收录,不算跳过 - 文件里出现未知段标签直接忽略
导入结果会逐段回报「新增 / 更新 / 未变 / 跳过」的条数。
词库导入导出
在设置 → 词库页选中方案后操作。
导出:勾选数据类型
导出对话框里勾选要导出的数据类型,全部写进同一个 .wdict.yaml(默认文件名 方案ID-时间戳.wdict.yaml)。可选类型随方案引擎类型变化:
| 方案类型 | 默认导出的段 |
|---|---|
| 码表 | 用户词库、临时词库、词频、候选调整 |
| 拼音 | 用户词库、临时词库、词频 |
| 混输 | 候选调整 |
拼音家族共用一份数据
全拼、双拼、混输的拼音子方案在存储上折叠为同一个数据域(pinyin)。导出双拼方案得到的就是整个拼音家族的数据,导入亦然。
导入:三种格式自动识别
导入不要求你选格式,按文件内容判定:
| 判据(按顺序) | 判为 |
|---|---|
头部(首个 --- 分隔行之前)含 wind_dict: | WindDict |
存在一整行只有 ... | Rime 词库 |
任一非空、非 # 开头的行含制表符 | TSV 文本 |
| 以上都不满足 | 报错「无法识别的词库格式」 |
Rime 词库(.dict.yaml) —— 头部到 ... 分隔行为止。列取头部的 columns: 列表,缺声明则用 Rime 默认的 [text, code, weight]。编码列内部空白折叠为单个空格(ni hao → ni hao),空格作为音节真值保留下来。缺 text 或 code 的行跳过;权重支持浮点,截断取整;解析失败回退 0。没有 ... 分隔行则报错。
TSV 文本 —— 每行 编码<TAB>词条[<TAB>权重],# 开头为注释。以下行跳过:列数少于 2、编码或词条为空、编码含非可打印 ASCII(这一条用来拦「词在前、码在后」的列序颠倒文件与乱码文件)。权重列可省,缺省 0。
Rime / TSV 只能导入用户词库
这两种外部格式没有词频、候选调整等概念,导入时一律只写入用户词库一段。要迁移完整数据请用 WindDict 格式。
跨引擎类型校验
WindDict 文件头带 engine_type 时,导入端会与目标方案比对,不一致直接拒绝:
该文件为「码表」类型词库,与当前「拼音」方案不一致,导入会导致编码错乱,已阻止。
五笔的 ggll 与拼音的 ni hao 属于两套编码域,混进去只会得到一堆永远打不出来的词条。老文件没有这个头部字段时不校验,照常导入。
合并与替换
| 策略 | 语义 |
|---|---|
| 合并导入(默认) | 保留本地已有条目,同键的以导入数据为准更新(count 取两边较大值) |
| 替换导入 | 先清空该方案对应的数据段,再写入 |
去重键:用户词库 / 临时词库 / 词频 / 候选调整为「方案 + 编码 + 文本」,短语为「编码 + 文本」。
导入前会先跑一次预览(不落盘),回报文件含哪些段、各段将新增 / 更新 / 未变 / 跳过多少条,确认后才执行。
快捷短语导入导出
在设置 → 词库 → 快捷短语中导入导出,格式同为 WindDict,只含 phrases 一段(默认文件名 phrases.wdict.yaml)。短语独有 position 列(同编码同权重时的先后),导入会保留该列的值。详见自定义短语。
方案包(.zip)
单个方案连同其引用资源打成的分发包。在设置 → 方案页选中方案后用「导出」/「导入」。
包内布局
zip 条目名 = 用户方案目录(schemas/)下的相对路径,没有任何目录前缀,导入时原样落盘、零改写:
package.toml 可选元信息(导出恒写,导入不强制)
wubi86.schema.toml 方案文件(根条目)
wubi86/wubi86_ext.dict.yaml 引用的词库
wubi86/HeiTiZiGen.ttf 引用的字根字体
shuangpin/my_layout.toml 引用的自定义双拼布局零前缀是刻意的:方案文件里对词库、双拼布局、拆字表、字体的引用都是相对路径,保持同样的相对结构,导入端不需要重写任何一条引用。
package.toml
[package]
app_version = "0.114.0"
platform = "windows"
created_at = "2026-08-03T10:00:00+08:00"
[schema]
id = "wubi86"
version = "1.2"
[refs]
system = ["wubi86/wubi86_jidian.dict.yaml"]
missing = []全部字段可缺省——导入端读不到就显示「未知」。[refs] 记录两类不在包内的引用:
system—— 指向程序自带数据目录的引用。这类文件人人都有,不打包,只记路径missing—— 导出时就已经找不到的引用,供接收方排查
资源收集规则
导出时解析方案文件里的全部引用路径,按三类处理:
| 命中位置 | 处理 |
|---|---|
| 用户数据目录 | 打包进 zip |
| 程序自带数据目录 | 记入 refs.system,不打包 |
| 都找不到 | 记入 refs.missing |
词库另有一条约定:引用写的是 x.dict.yaml、但用户目录下只有编译好的 x.wdat 时,打包同名的 .wdat,包内条目名也随之改成 .wdat——否则接收方会得到一个名叫 yaml、内容却是二进制的文件,两头都读不了。
导入判据
- 根目录下有
*.schema.toml→ 认为是方案包(没有package.toml也能导入,方便手工打包) - 根目录下没有任何
.schema.toml→ 拒绝,提示「不是有效的方案包」 - 含
manifest.toml/manifest.json→ 拒绝,提示误选了整机备份包或旧格式归档
导入前预览显示:方案 ID 与版本、创建时间、新增文件 / 已存在 / 系统引用 / 缺失各多少个。随后选择:
- 合并 —— 跳过已存在的同名文件(计入「已存在」数)
- 替换 —— 覆盖同名文件
写盘用「先写临时文件再改名」,中途失败不会留下半个文件。
方案包不含个人数据
方案包只有方案配置与引用资源,不含你的用户词、词频、候选调整。把方案分享给别人不会连带泄露个人输入记录;换机时要带走个人数据请用整机备份。
整机备份(.zip)
设置 → 高级 → 备份与还原。操作流程见备份与还原,这里只讲格式。
manifest.toml
包根目录下的自描述清单,还原前免解压即可读取并显示摘要:
format = "windinput-bundle"
kind = "backup"
spec_version = 1
app_version = "0.114.0"
platform = "windows"
created_at = "2026-08-03T10:00:00+08:00"
[[contents]]
type = "dict"
path = "userdata/user_words/wubi86.wdict"
[contents.meta]
schema = "wubi86"| 字段 | 说明 |
|---|---|
format | 恒为 windinput-bundle,不匹配即拒绝 |
kind | backup(整机备份)。备份页只接受这个值 |
spec_version | 归档格式版本,当前 1。高于当前支持的版本直接拒绝并提示升级清风 |
app_version / platform / created_at | 来源版本、平台(windows / darwin)、创建时间 |
contents | 条目清单:每项的 type、包内 path、可选 meta(如所属方案) |
包内布局
manifest.toml
config/config.toml type="config"
userdata/user_words/<方案>.wdict type="dict" meta.schema
userdata/temp_words/<方案>.wdict type="temp" meta.schema
userdata/freq/<方案>.jsonl type="freq" meta.schema
userdata/shadow/<方案>.jsonl type="shadow" meta.schema
userdata/phrases.wdict type="phrase"
userdata/stats.jsonl type="stats" (勾选「包含统计数据」时)
userdata/stats_meta.json type="stats_meta" (同上)
schemas/<用户方案目录整树> type="schema_file"
themes/<用户主题目录整树> type="theme_file"
state/state.toml type="state" (勾选「包含界面状态」时)用户数据逐表导出为文本而非直接搬数据库文件:词库与短语用 WindDict 格式,词频、候选调整、统计用 JSON Lines(每行一个 JSON 对象)。这样跨版本、跨平台都稳,也避开了 Windows 上数据库文件被占用的麻烦。
方案与主题按整目录树原样打包;数据表按方案拆分子目录,归属由 manifest 条目的 meta.schema 标注——因此有数据但已停用的方案也会被备份到。
缓存与日志不进包
%LOCALAPPDATA%\WindInput\ 下的 cache/、logs/ 一律排除:前者可从词库源文件重建,后者无迁移价值。state.toml(窗口位置等界面状态)本机相关,默认也不含,需要时勾选「包含界面状态」。
还原范围与策略
还原时可勾选还原范围,对应清单条目的 type:
| 勾选项 | 覆盖的 type |
|---|---|
| 配置 | config |
| 词库数据 | dict、temp、phrase、freq、shadow |
| 统计 | stats、stats_meta |
| 用户方案 | schema_file |
| 主题 | theme_file |
| 界面状态 | state |
策略在文件域与数据域上语义不同:
| 合并还原 | 替换还原 | |
|---|---|---|
| 文件域(配置、方案、主题、界面状态) | 已存在的文件跳过,计入冲突数 | 覆盖同名文件 |
| 数据域(词库、短语、词频、候选调整) | 逐条 upsert 合并 | 先清空该域再导入 |
| 统计 | 已存在的日期跳过 | 先清空再导入 |
还原完成后自动重载配置、重建短语与受影响方案的缓存,无需重启。
安全限制
三处导入都对归档条目名做了统一守卫,只放行「普通路径段」:拦截 ..、绝对路径、盘符相对路径(C:foo)、UNC 路径,以及段内含冒号的名称(Windows 的 NTFS 备用数据流)。构造恶意 zip 无法把文件写到用户数据目录之外。
从 HTTPS 链接导入主题时会先交给核心程序校验内容再落盘;导入前对话框会提示确认来源可信。
跨平台还原不做自动转换
备份包记录了来源平台,但热键、应用规则里的路径这类平台专属项不会自动转换。从 Windows 的备份还原到 macOS(或反之)时,这些项需要自行复核。