easy-word 的多编码文本读取:BOM 检测与编码自动识别
导入一份从 Windows 记事本导出的单词表,界面上出现了一堆 锟斤拷。排查发现,文件是 GBK 编码,而代码直接用 TextDecoder('utf-8') 解码。
这个问题在教育类应用里特别常见。老师用的电脑五花八门,导出的文本文件编码也不统一。easy-word 虽然内置词库是 JSON 格式(统一 UTF-8),但用户手动导入单词表的场景迟早要做。与其到时候一个个修 bug,不如先把编码检测的逻辑理清楚。
编码检测的优先级
拿到一个文件,先读前几个字节判断编码:
1 | 字节序列 判断结果 |
BOM(Byte Order Mark)是编码格式的”身份证”。有 BOM 的文件,编码是确定的。问题出在没有 BOM 的文件上——这时候只能靠试探。
没有 BOM 时的判断逻辑
鸿蒙的 util.TextDecoder 只做解码,不帮你判断编码。判断需要自己写:
1 | import { util } from '@kit.ArkTS'; |
核心思路:先用 UTF-8 解码,检查是否出现替换字符 \uFFFD。没出现说明 UTF-8 解码正常;出现了说明字节序列不合法,大概率是 GBK。
为什么 UTF-8 优先
GBK 也可以解码 UTF-8 的中文,但反过来不行。UTF-8 的中文是 3 字节,GBK 的中文是 2 字节,UTF-8 解码器遇到 GBK 的 2 字节序列会产生无效序列。反过来,GBK 解码器遇到 UTF-8 的 3 字节序列会把它拆成”2字节 + 1字节”,结果是乱码但不报错。
所以判断顺序很重要:先试 UTF-8,失败了再试 GBK。不能反过来。
HarmonyOS 的 TextDecoder 限制
鸿蒙的 util.TextDecoder 支持的编码比 Node.js 少。实际可用的:
| 编码 | 是否支持 |
|---|---|
| utf-8 | ✅ |
| utf-16le | ✅ |
| utf-16be | ✅ |
| gbk | ⚠️ 需要系统级支持 |
GBK 的支持取决于设备系统。华为自有设备基本都支持,但模拟器和部分第三方设备可能不行。代码里需要兜底:
1 | let decoder: util.TextDecoder; |
WordLibraryModel 的编码策略
当前 easy-word 的词库加载比较严格——所有 JSON 文件都用 UTF-8 编码打包进应用。这是正确的做法:
1 | // entry/src/main/ets/service/WordLibraryModel.ets |
内置资源不需要编码检测,因为开发者完全控制编码。多编码处理留给用户导入场景。
实际应用中的取舍
编码检测不是万能的。有些情况会误判:
- 纯 ASCII 文件:UTF-8 和 GBK 解码结果完全一样,无法区分。实际上也不需要区分。
- 混合编码文件:同一个文件里既有 UTF-8 又有 GBK。这种情况只能手动处理,没有通用解法。
- 短文本:文件太小(比如只有几个英文单词),UTF-8 验证不够可靠。
对于 easy-word 来说,最实用的策略是:内置词库统一 UTF-8,用户导入时做编码检测,检测不出来就默认 UTF-8 让用户手动选择。
编码问题的本质是:字节本身没有意义,解释方式才有。同一个字节序列,UTF-8 和 GBK 会给出完全不同的解读。
下一篇会讲 tts-tool 如何处理 WAV 文件的 RIFF header 手写编码,以及 Base64 和二进制双响应的设计。