更新记录

1.0.0(2026-10-30)

首个版本:六类乱码可疑点逐行定位(替换符/锟斤拷双重转码/单字节误读/生僻字扎堆/控制字符/丢字问号),同类型相邻命中合并计数;可疑度 0–100 分三档并给处置建议;编码探测走 BOM → 严格 UTF-8 试解 → GBK 三步并回传判断依据;丢字预扫按 CharsetEncoder.canEncode 逐字符判定,输出字符/次数/行号并按次数排序;转换按 from 解码、to 编码写出,同时落 .loss.csv 丢字清单;体检报告导出 CSV / 纯文本,UTF-8 带 BOM。UTS→Kotlin 编译通过(index.kt 1447 行、classes.dex 59,008 字节),98 项回归测试全部通过;真机未执行(测试机安全锁屏,未提供解锁口令),市场截图为 H5 渲染并标注示例数据。


平台兼容性

uni-app

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× √ × × √ × √ × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × - × - × × × ×

编码转换与丢字预检(乱码识别)

给对接老系统、导老报表的你:转码之前先体检。

老 ERP 导出 GBK、新系统只吃 UTF-8,直接转过去两种结果:要么整屏乱码(锟斤拷、烫烫烫), 要么静悄悄少了几个字(生僻名字、镕、昇、㈱)。第二种最坑——文件能打开、行数也对, 等你把数据导进财务系统、对完账才发现某个人名是空的。

这个插件把「转码」拆成三步,每一步都能看结果:

  1. 体检:这段文本现在是乱的还是好的?0–100 分,逐行指出可疑点;
  2. 预扫:转成目标编码会丢哪些字?字符、次数、行号,一次列清;
  3. 转换:确认没问题再写文件,转换结果和丢字清单一起交回。

给谁用

  • 实施/运维:客户现场导出的 CSV 进不了新系统,先确认是乱码还是丢字;
  • 财务/仓管:老进销存导月报,GBK 转 UTF-8 后金额没问题但客户名对不上;
  • 开发:接口联调收到一堆 锟斤拷,需要快速判断是哪一段被双重转码;
  • 数据迁移:从历史系统批量导出,转码前先出一份「会丢什么」的清单给客户确认。

能做什么

能力 说明
乱码体检 六类可疑点:替换符 U+FFFD、锟斤拷 双重转码、单字节误读(ä 型)、生僻字扎堆、控制字符、连续问号
可疑度打分 0–100,三档结论:干净 / 可疑 / 已乱码,附一句该怎么处理
逐行定位 每条可疑点带行号与上下文片段,同一行相邻同类点自动合并计数,不刷屏
编码探测 BOM 优先 → 严格 UTF-8 试解(非法字节即判失败)→ 退回 GBK,并说明按什么判断出来的
丢字预扫 逐字符问目标编码「你能表示吗」,列出字符 / 次数 / 行号,按出现次数排序
转换 按 from 解码、按 to 编码写出,默认再落一份 .loss.csv 丢字清单
报告导出 体检结果出 CSV(类型 / 行号 / 字符 / 片段 / 说明)或纯文本摘要,带 BOM,Excel 双击不乱码

快速接入

import { check, probe, loss, convert, exportReport } from '@/uni_modules/szhzy-encoding-guard'

// 1. 体检一个文件(不传 charset 就自己探)
const r = check({ path: '/storage/emulated/0/Download/客户台账.csv' })
// r.ok / r.score / r.verdict / r.charset / r.guess / r.suspects[]

if (r.score >= 50) {
  // 已经乱了:别在这份文件上转码,回源头重导
  console.log(r.advice)
}

// 2. 只想知道这是什么编码
const p = probe({ path: '/storage/emulated/0/Download/客户台账.csv' })
// p.charset = 'GBK',p.guess = 'UTF-8 解不通,按 GBK 读'

// 3. 转码前先看会丢什么
const l = loss({ path: p.path, from: p.charset, to: 'UTF-8' })
// l.total = 0 → 一个都不丢,放心转
// l.chars = [{ ch: '昇', count: 3, lines: [12, 40, 91] }, ...]

// 4. 确认后转换,丢字清单自动落盘
const c = convert({
  path: '/storage/emulated/0/Download/客户台账.csv',
  from: 'GBK',
  to: 'UTF-8',
  fileName: '客户台账-utf8.csv'
})
// c.path 新文件路径;c.losses 丢字清单(同时写了 客户台账-utf8.csv.loss.csv)

// 5. 体检报告导成 CSV 交给客户
exportReport({ path: '...', format: 'csv', fileName: '编码体检.csv' })

粘一段文本直接测(不用文件)

const r = check({ text: '鎴戜滑鏄庡ぉ绛炬?    ' })
r.score     // 分数高 → 这段是双重转码过的
r.suspects  // [{ line: 1, kind: 'double', label: '双重转码', snippet: '...' }]

API

方法 入参 返回
check { path } 或 { text, bom? },可加 charset、gap、maxSuspects { ok, score, verdict, verdictText, advice, charset, guess, bytes, stats, suspects[] }
probe { path, charset? } { ok, charset, guess, bom, bytes, sizeText, chars }
loss { text } 或 { path, from? },加 to(默认 GBK) { ok, target, total, kinds, chars: [{ ch, count, lines[] }] }
lossCsv { text, to, delimiter? } { ok, csv, total, kinds }
convert { path, from?, to, dir?, fileName?, writeLoss?, delimiter? } { ok, path, fileName, bytes, sourceCharset, targetCharset, losses }
exportReport { path 或 text, format: 'txt'\|'csv', dir?, fileName?, bom? } { ok, path, bytes, sizeText }
charsets — 支持清单(UTF-8 / GBK / GB18030 / UTF-16LE)
getOutputDir — 应用外部私有目录绝对路径
getVersion — 版本号

所有方法返回统一信封 { ok, code, message, ... },不抛异常。

返回里的 stats

字段 含义
chars / lines / bytes 字符数(UTF-16 单元)、行数、按 UTF-8 计的字节数
replaceCount U+FFFD 替换符个数——大于 0 说明有过不可逆的丢字
controlCount 除 \t \r \n 外的控制字符个数
rareCount / rarePct 生僻区汉字(CJK 扩展 A、兼容区)个数与占比(千分数)
questionRuns 连续 3 个以上问号出现的次数——通常是上一次转码丢字的残留
hasBom / nonBmp 是否有 BOM、BMP 外字符(emoji、扩展 B 汉字)个数

判定规则是写死的,不是拍脑袋

可疑点 分值 依据
U+FFFD 每个 10 分,最多 80 解码失败才会产生替换符,出现即已丢字
锟斤拷 每处 20 分,最多 40 UTF-8 被当 GBK 读、再存回 UTF-8 的固定残渣
烫烫烫 / 屯屯屯 每处 25 分 未初始化内存被当字符串写出,导出端 bug
单字节误读 每处 4 分,最多 20 Ã Â ï 后跟非 ASCII,典型的「UTF-8 当单字节读」
生僻字扎堆 占比 ≥10% 加 15 分,≥25% 加 25 分 正常业务文本不会大段是扩展 A 区汉字
控制字符 每处 6 分,最多 18 粘贴进系统容易截断字段
连续问号 每处 4 分,最多 12 不可表示字符被换成 ? 的痕迹

合计上限 100:<20 干净、20–49 可疑、≥50 已乱码。

边界与限制

  • 只处理纯文本。Word / Excel / PDF 请先用 szhzy-docx-read、szhzy-xlsx-import 抽成文本再进来;
  • 编码探测只覆盖 UTF-8 / GBK / BOM 三条路。UTF-16(无 BOM)、Big5、Shift-JIS 认不出, 这时会明说「UTF-8 解不通,按 GBK 读」,请调用方显式传 from;
  • 丢字预扫按字符问 CharsetEncoder.canEncode,结果与 Java 的编码表完全一致, 但遇到「某些系统用私有区映射生僻字」的情况,预扫会说能表示、对方系统仍可能显示成方框;
  • 转码不修数据。已产生的 锟斤拷 不可逆,插件只负责告诉你「这里有、在第几行」,不猜原字;
  • 单文件上限 20 MB(读字节保护),超过直接返回 READ_FAILED,请先切分。

平台

仅 Android(utssdk/app-android)。已用 UTS→Kotlin 编译通过,详见 接入示例.md 与 测试记录.md。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无系统权限。读取调用方用系统文件选择器交过来的文件(路径或 content:// 授权),写出走应用外部私有目录。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集、不上传任何数据,不访问网络,不写日志。文本只在本机内存中判断与转换;结果文件由调用方决定是否写出,默认写应用外部私有目录。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。