JS读取Excel出现中文乱码怎么解决

作者:小括号时间:2026-11-05 22:02:33浏览:520

针对JavaScript在前端读取Excel文件(.csv、.xlsx、.xls)时出现的各类中文乱码问题,深入剖析字符集编码冲突与二进制解析机制,提供基于FileReader、TextDecoder与SheetJS的标准解决方案与完整代码范例。

前端技术 文件处理 字符编码 阅读时长约 8 分钟 适用环境:现代浏览器 / Node.js
JS读取Excel出现中文乱码怎么解决
架构总览:JavaScript 读取 Excel 文件的字符集解码与二进制解析流程架构
JavaScript 读取 Excel 文件出现中文乱码,核心原因只有两类:一是将 .csv 文件当作普通文本读取时字符编码不匹配(Windows 下 Excel 默认保存的 CSV 通常为 ANSI/GBK 编码,而前端默认以 UTF-8 解码);二是对 .xlsx 或 .xls 复合二进制工作簿误用了纯文本读取 API(例如 FileReader.readAsText),导致二进制压缩包字节流被强行转换为乱码字符。解决路径十分明确:区分文件真实格式,CSV 文件指定 'GBK' 或通过 TextDecoder 动态解码并剥离 BOM 头,XLSX 文件则必须通过 FileReader.readAsArrayBuffer 读取原始二进制字节并交由 SheetJS 等专业解析库处理。

一、乱码根源诊断:为什么 JS 读 Excel 会变乱码?

在排查乱码前,必须先理清所谓的“Excel 文件”在底层存在两种截然不同的物理形态:

  1. CSV 纯文本表格(.csv):虽然双击会默认由 Microsoft Excel 打开,但它本质上是纯文本文件,使用逗号分隔数据。在中文 Windows 环境下,通过 Excel 另存为 CSV 时,软件默认采用的是操作系统的本地编码(即 ANSI / Windows-936 / GBK),其存储的中文字符占两个字节(例如汉字“张”编码为十六进制 D5 C5)。而现代浏览器中的 FileReader.readAsText(file) 若不传第二个参数,默认强制使用 UTF-8 规则进行解码。UTF-8 判定该字节序列不符合多字节 UTF-8 编码规则,直接将其标记为非法字节,替换为 Unicode 替换字符 (U+FFFD),表现为满屏的黑色菱形问号,或在二次转码中退化为“锟斤拷”。
  2. 现代工作簿(.xlsx)与旧版工作簿(.xls):.xlsx 文件的本质是一个经过 ZIP 算法压缩的 XML 文件包(OpenXML 标准),其文件头部前四个字节固定为 50 4B 03 04(即 ASCII 字符 PK..)。如果开发者直接对其调用 FileReader.readAsText(),浏览器会把这批纯粹的压缩二进制数据当成文本尝试解码,结果必然是输出一段以 PK.... 开头的混乱乱码,不仅汉字全无,任何单元格结构也彻底丢失。
JS读取Excel出现中文乱码怎么解决
图 1:CSV 文本编码冲突与 XLSX 二进制误读机制对比图

二、场景一:读取 CSV 格式 Excel 乱码的解决方法

如果上传的是 .csv 文件,出现中文乱码是因为字符集解码器选错。解决此问题有以下三种具体做法。

方法 1:为 FileReader.readAsText 指定 GBK / GB18030 编码

FileReader.prototype.readAsText() 方法接受第二个可选参数 encoding。当明确知道用户来自中文 Windows 环境时,将其显式设置为 'GBK' 或兼容性更广的 'GB18030' 即可直接解决乱码:

function readCsvFileGbk(file) {
  const reader = new FileReader();
  
  reader.onload = function(e) {
    const text = e.target.result;
    // 此时 text 中的中文字符已正确映射为 JavaScript 内部的 Unicode 字符串
    console.log("解析出的正常文本内容:", text);
  };

  // 关键点:第二个参数必须指定为 'GBK' 或 'gb18030',切勿留空
  reader.readAsText(file, 'GB18030');
}
提示:GB18030 是国家强制标准编码,向下兼容 GBK 和 GB2312,不仅包含了常用汉字,还收录了藏、蒙、维等少数民族文字及冷僻姓名汉字。因此在指定中文编码时,传入 'GB18030' 比 'GBK' 具备更好的防御性。

方法 2:使用 readAsArrayBuffer + TextDecoder 自适应处理

现实场景中,用户的 CSV 文件可能是 Windows 导出的 GBK 编码,也可能是 Mac 或外部导出的标准 UTF-8 编码。为了实现一套代码自动兼容,应采用 readAsArrayBuffer 先读取原始字节流,通过检测文件开头的 BOM 标志(0xEF 0xBB 0xBF)来判断是 UTF-8 还是 GBK,再利用原生 TextDecoder 进行解码:

function readCsvAdaptive(file) {
  const reader = new FileReader();

  reader.onload = function(e) {
    const buffer = e.target.result;
    const bytes = new Uint8Array(buffer);

    // 1. 检测是否带有 UTF-8 的 BOM 头 (0xEF, 0xBB, 0xBF)
    const hasUtf8Bom = bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF;

    let decodedString = "";
    if (hasUtf8Bom) {
      // 若有 BOM 标记,使用 utf-8 解码
      const decoder = new TextDecoder('utf-8');
      decodedString = decoder.decode(bytes);
    } else {
      // 无 BOM 标记且出现中文乱码,优先采用 GB18030 解码
      try {
        // 开启 fatal: true 可探测是否为严格合法的 UTF-8
        const utf8Decoder = new TextDecoder('utf-8', { fatal: true });
        decodedString = utf8Decoder.decode(bytes);
      } catch (err) {
        // 若非合法 UTF-8,平滑回退至 GB18030
        const gbkDecoder = new TextDecoder('gb18030');
        decodedString = gbkDecoder.decode(bytes);
      }
    }

    // 2. 清洗可能存在的 BOM 首字符()
    decodedString = decodedString.replace(/^/, '');
    console.log("自适应解码结果:", decodedString);
  };

  reader.readAsArrayBuffer(file);
}

方法 3:剔除 BOM 隐形乱码字符()

部分 CSV 文件虽然汉字能正常看到,但在通过属性名读取对象时(如 item["姓名"])却返回 undefined。这是因为文件首部带有 UTF-8 字节序标记 BOM(),它虽然在屏幕上是不可见的零宽字符,但会粘连在第一列的列名上变成 姓名。因此在解析任何 CSV 第一行表头时,必须执行:

const sanitizedContent = rawCsvText.replace(/^/, '');

三、场景二:读取 XLSX / XLS 格式工作簿乱码的解决方法

.xlsx 绝不能使用 readAsText(),任何字符集参数都无法拯救二进制 zip 包。读取 .xlsx 必须走**二进制读取 + 工作簿解构**流程。社区中最成熟的工业标准库是 SheetJS(又称 xlsx)。

1. 正确引入与核心配置

在 HTML 中引入官方库(或通过 npm 安装 npm install xlsx):

<!-- 生产环境引入 xlsx 核心库 -->
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>

2. 使用 readAsArrayBuffer 进行正确解析

前端通过 FileReader.prototype.readAsArrayBuffer 获取文件底层原始字节数组,然后传给 XLSX.read,并显式指定 type: 'array'。此流程下,SheetJS 会自动解开 ZIP 压缩包,解析内部的 sharedStrings.xml 字符串池,绝不会产生乱码:

function readExcelWorkbook(file) {
  const reader = new FileReader();

  reader.onload = function(e) {
    const arrayBuffer = e.target.result;
    
    // 必须传入 type: 'array' 告知库传入的是 ArrayBuffer 二进制内存块
    const workbook = XLSX.read(arrayBuffer, {
      type: 'array',
      cellDates: true  // 顺便将日期数字转为 Date 对象
    });

    // 提取第一个工作表
    const firstSheetName = workbook.SheetNames[0];
    const worksheet = workbook.Sheets[firstSheetName];

    // 将单元格转化为标准 JSON 数组,中文表头与内容均完美保留
    const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: "" });
    console.log("解析出的工作表数据:", jsonData);
  };

  reader.readAsArrayBuffer(file);
}
旧版 .xls 补充说明:若遇到的是 1997-2003 年代生成的老旧 .xls 二进制文件(BIFF8 格式),如果解析出来中文有少量偏僻乱码,需确保使用的是包含代码页支持的 xlsx.full.min.js,并在选项中附加 codepage: 936。精简版 xlsx.mini.min.js 默认不带 codepage 映射表。

四、系统化排查与决策流程

当遇到 Excel 乱码问题时,切忌盲目更换 API。应遵循下方的技术决策树,先判断容器类型(纯文本还是压缩二进制),再确定字符编码或解析管道:

JS读取Excel出现中文乱码怎么解决
图 2:JavaScript 读取 Excel 中文乱码故障排查与修复决策流程图

五、完整工程实现:通用 Excel / CSV 无乱码解析组件

将上述逻辑封装为一个兼顾 .xlsx、.xls 和 .csv 的统一读取函数,前端调用方只需传入 File 对象,即可直接获得无乱码的 JSON 数组。

JS读取Excel出现中文乱码怎么解决
图 3:前端文件捕获、编码分流、结构化转换与界面渲染步骤时序图
/**
 * 通用 Excel 读取工具:支持 xlsx, xls, csv,全面防御中文乱码
 * @param {File} file 用户选择的文件对象
 * @returns {Promise<Array<Object>>} 解析后的 JSON 数据行
 */
async function parseExcelFile(file) {
  if (!file) throw new Error("未选择有效文件");

  const fileName = file.name.toLowerCase();
  const arrayBuffer = await file.arrayBuffer();

  // 场景 1:如果是 CSV 文本文件
  if (fileName.endsWith('.csv')) {
    const bytes = new Uint8Array(arrayBuffer);
    let text = "";

    // 优先按 UTF-8 严格模式尝试解码
    try {
      const utf8Decoder = new TextDecoder('utf-8', { fatal: true });
      text = utf8Decoder.decode(bytes);
    } catch (e) {
      // 捕获到非法 UTF-8 字节序列(Windows 导出的 ANSI/GBK),回退至 GB18030
      const gbkDecoder = new TextDecoder('gb18030');
      text = gbkDecoder.decode(bytes);
    }

    // 剔除首行开头的 UTF-8 BOM 字符
    text = text.replace(/^/, '');

    // 利用 SheetJS 或自建正则将 CSV 纯文本转换为结构化工作表
    const csvWorkbook = XLSX.read(text, { type: 'string' });
    const firstSheet = csvWorkbook.Sheets[csvWorkbook.SheetNames[0]];
    return XLSX.utils.sheet_to_json(firstSheet, { defval: "" });
  }

  // 场景 2:如果是 .xlsx 或 .xls 二进制工作簿
  if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
    const workbook = XLSX.read(arrayBuffer, {
      type: 'array',
      codepage: 936, // 补齐中文代码页映射
      cellDates: true
    });
    
    const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
    return XLSX.utils.sheet_to_json(firstSheet, { defval: "" });
  }

  throw new Error("不支持的文件格式,仅支持 .csv, .xlsx, .xls");
}

六、验证方法与常见避坑要点

  • 如何检验乱码是否彻底修复? 不仅要肉眼观察页面上的文字,还应在控制台打印 Object.keys(row[0])。检查首个字段键名是否带有多余的空宽字符,字符串长度是否与汉字数量一致。
  • 避免二次导出引发“乱码循环”: 如果在前端生成 CSV 供用户下载,记得在写入字节流的开头显式加入 UTF-8 BOM (例如 new Blob(['' + csvString], { type: 'text/csv;charset=utf-8;' }))。否则 Windows Excel 再次双击打开该文件时,依然会因为缺少 BOM 标记而误用 ANSI 打开,导致用户看到乱码。
  • Node.js 服务端环境适配: 若在 Node.js 环境下使用 fs.readFileSync(path) 读取,若文件为 GBK 编码的 CSV,不可直接 .toString('utf-8'),应配合 iconv-lite 库进行 iconv.decode(buffer, 'gbk') 解码。

七、全场景速查与总结

为了便于日常开发随时检索,将不同格式 Excel 文件的乱码表现、根本原因与对应技术参数整理如下表与速查卡片中:

JS读取Excel出现中文乱码怎么解决
图 4:JavaScript 读取 Excel 全场景根因与解决方案速查矩阵
文件类型 默认底层编码 典型乱码表现 关键解决 API / 方法 核心配置项
Windows CSV ANSI (GBK / CP936) 汉字变成 菱形问号 readAsText(file, 'GB18030') 编码指定为 'GB18030' 或 'GBK'
UTF-8 带 BOM CSV UTF-8 + 3字节文件头 首列表头读取 undefined 正则清除字符串开头 BOM text.replace(/^/, '')
Excel 2007+ (.xlsx) ZIP 压缩容器 (OpenXML) 乱码以 PK.... 开头 readAsArrayBuffer + SheetJS XLSX.read(buf, { type: 'array' })
Excel 97-2003 (.xls) BIFF8 二进制复合文档 个别中文单元格内容缺失/乱码 引入 full 版 SheetJS 配置项附带 codepage: 936
作者最新文章
相关文章
更多