前言
在做项目的时候发现支付宝账单是ANSI编码;微信账单是带BOM的utf-8编码。
这两个账单都是CVS文件,但在读取的时候ANSI发生了乱码。记录一下解决过程。
1. 了解 ANSI 与 utf-8 with BOM
(1)什么是 ANSI 编码?
不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的编码标准。这些使用多个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文Windows操作系统中,ANSI 编码代表 GB2312编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 JIS 编码。
(2)什么是 带BOM的utf-8 编码?
utf-8我们知道是基于UTF万国码的一种编码,那么BOM呢?
直接看wiki的定义:
字节顺序标记(英语:byte-order mark,BOM)是位于码点U+FEFF的统一码字符的名称。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字符串编码时,这个字符被用来标示其字节序。它常被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的标记。
字节顺序标记通常有几种涵义:
在16位和32位的情况下,文字流的字节顺序。
表示文字流非常有可能是Unicode编码。
使用的是哪一种Unicode字符编码。
每个Unicode编码(包括Unicode标准以外的编码,如UTF-7,见下表)的BOM字节序列都不一样,而且这些序列都不可能出现在以其他编码存储的文字流的开头。因此,在文字流的开头放置一个编码的BOM,可以表明文本是Unicode,并识别所使用的编码方案。这种对BOM字符的使用被称为“Unicode签名”。
2.解决导入cvs乱码
现在回过来看为什么导入的cvs会乱码
云存储在存储文件的时候是不会改变其文件编码的,而在云函数的 node 中,我使用的是 node-xlsx 来进行 excel 导入。从 node-xlsx 给出的例子来看,node-xlsx 是只接收 utf-8 文件的。那么,一个 ANSI (简中为GB2312)编码必然不能被 node-xlsx 解析,这就需要转成 utf-8 。
我们很自然的会想到用toString方法,再 Buffer.form 回去,但是 node 中这些方法都没有 GB2312 编码。
switch(encoding){
case 'hex':
case 'utf8':
case 'utf-8':
case 'ascii':
case 'binary':
case 'base64':
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
}
所以我们需要另一个插件 iconv-lite 转换。
核心代码:
//buffer根据原编码GB2312解码成字符串,不舍弃BOM
let sourceString = iconv.decode(sourceBuffer,"GB2312",{stripBOM: false})
//字符串转码成utf-8的buffer,添加BOM
let buff = iconv.encode(sourceString,"utf8",{addBOM: true});
代码片段:
const cloud = require('wx-server-sdk')
const xlsx = require('node-xlsx')
const iconv = require('iconv-lite');
cloud.init({ // 初始化云开发环境
env: cloud.DYNAMIC_CURRENT_ENV // 当前环境的常量
})
// 云函数入口函数
exports.main = async (event, context) => {
const { fileID } = event;
// 下载文件
const res = await cloud.downloadFile({ fileID })
let sourceBuffer = res.fileContent;
//buffer根据原编码GB2312解码成字符串,不舍弃BOM
let sourceString = iconv.decode(sourceBuffer,"GB2312",{stripBOM: false})
//字符串转码成utf-8的buffer,添加BOM
let buff = iconv.encode(sourceString,"utf8",{addBOM: true});
//开始解析
let sheet = xlsx.parse(buff,{ cellDates: true })[0];
return sheet;
}
至于为什么需要BOM,其实即使不使用BOM,字符串的输出也没有乱码了,但是excel插件没有BOM,解析不行。大胆推测一下,插件需要BOM来正确的解析excel。
3. 注意
我看网上很多人的代码在字符串已经出现乱码的情况下,还要进行转码,这就非常离谱了,你把乱码转成另外一种编码有什么用呢?解码出来不还是乱码吗?这完全是对编码解码与buffer之间关系的不理解。
4. 参考
https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%85%83%E7%B5%84%E9%A0%86%E5%BA%8F%E8%A8%98%E8%99%9F