文件导入为什么会出现乱码
2022-03-30 23:40:27 # fontend

最近在做一个导入文件并解析的需求,发现导入文件之后偶现乱码的问题。针对这个问题,小小的定位了一下。

如何读取文件

1
2
3
4
5
6
7
8
9
inputFileDom.addEventListener('change', (event) => {
const file = event.target.files[0];
const fileReader = new FileReader();
fileReader.onload = (info) => {
const fileStr = info.target.result; // 会出现乱码
doSomething(fileStr);
};
fileReader.readAsText(file);
})

fileReader 的 read 相关操作,其实都还有第二个可选参数,就是字符编码类型,默认都是 UTF-8,如果说导入的文件出现了乱码,就说明编码格式不对,或者说文件缺少字符,导致编码识别错误,出现乱码。

为什么会乱码

首先文件在计算机上的存储物理上其实都是二进制的,而文本文件和二进制文件只是逻辑上的区别,文本文件是基于字符编码的,常见的字符编码有 ASCII 编码,Unicode 编码等等,而二进制文件是基于值编码的文件。文本文件通常可以用文本编辑器打开,他们的区别就是编码不同。而文件的乱码通常都是因为编/解码格式不一样,所以出现了乱码。

前端中常见的就是文本文件的乱码,因为文本文件通常是可以预览的,二进制基本不能用文本编辑器或者输入框来预览。比如 html 文件在预览的时候为啥会出现乱码? 因为文件在进行编码转换前,都会对文本文件的编码格式进行探测,常用的编码探测方法为根据文本文件对应的编码子节序标识,也就是文本的前几个字节进行判定,例如前三个字节为 EFBBBF,则为 UTF-8 编码格式;若前两个字节为 FEFF 则为 UTF-16BE 编码格式,若为 FFFE 则为 UTF-16LE 编码格式等。然而,这样的方法并不总是可行的,对于有些编码格式,例如 ASCII、GB2312 和 UTF7 编码等都没有编码字节序标识,对于这样的编码格式,通常采用给一个默认的编码格式进行编码转换,但默认的编码格式与文本文件的编码格式不一致则会使文本文件显示为乱码。

如何解决乱码

可以读取文件的编码格式,然后在 read 相关操作的时候设置为该编码格式。这里读取文件的编码格式需要用到第三方包 jschardet : 读取数据的编码格式, iconv-lite 可以根据指定编码,解码数据。

1
2
3
4
5
6
7
const fileReader = new FileReader();
fileReader.onload = async (event: any) => {
const buffer = Buffer.from(event.target.result);
const type = jschardet.detect(buffer);
const res = iconvlite.decode(buffer, type?.encoding || 'utf-8');
};
fileReader.readAsArrayBuffer(info);

注意: 以上的做法,只能说解决大部分乱码的问题,因为如果编码格式本身系统不支持或者浏览器不支持的话,该乱码的还是乱码。