在Node.js生态中,处理Excel文件最常用的库之一就是Exceljs。它功能强大,支持读写.xlsx、.xlsm等格式,但许多开发者都曾遇到一个令人困惑的情况:明明Excel单元格里显示的是文本"123",用Exceljs读取时却返回了数字123;或者读取日期时,返回的是一串数字时间戳。这种“类型自动转换”常常导致后续字符串拼接、数据校验等逻辑出错。
那么,如何正确地获取单元格的原始字符串值?本文将深入解析Exceljs的读取机制,并提供一份可靠的解决方案。
一、问题重现:为什么getValue()不总是字符串?
先看一段典型的代码:
const ExcelJS = require('exceljs');
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile('sample.xlsx');
const worksheet = workbook.getWorksheet(1);
const cell = worksheet.getCell('A1');
console.log(cell.value); // 可能返回 123 (number) 或 "2023-01-01T00:00:00.000Z" (Date)
Exceljs的cell.value返回的是JavaScript原生类型。如果单元格格式为“常规”或“数字”,它就会自动解析为number;如果格式为日期,则解析为Date对象。这种智能解析在大多数场景下很方便,但在需要保留原始输入字符串(例如身份证号、电话号码、数字编码)时就成了障碍。
二、核心方案:使用 cell.text 属性
Exceljs其实已经提供了获取格式化显示字符串的属性——cell.text。这个属性返回的是单元格在Excel中实际显示的文本,保留所有格式,且永远是字符串。
示例:
const cell = worksheet.getCell('B2');
console.log(cell.text); // 例如 "00123"(即使原始值是数字123)
console.log(typeof cell.text); // "string"
对于日期单元格,cell.text会返回Excel格式化后的日期字符串(例如“2024/1/15”),而不是时间戳。对于带有自定义格式(如“#,##0.00”)的数字,也会返回格式化后的文本。
注意事项:
cell.text依赖于单元格的numFmt(数字格式)。如果单元格没有指定格式,可能会返回科学计数法等奇怪结果。- 对于公式单元格,
cell.text返回的是计算后的显示结果,而不是公式本身。 - 如果单元格为空,
cell.text返回空字符串""。
三、进阶方案:显式获取原始字符串值
在某些极端情况下,你可能需要读取Excel文件中存储的原始字符串,而不经过任何格式化处理。这时可以使用cell.value的自带属性:
const value = cell.value;
if (value === null) {
// 空单元格
} else if (typeof value === 'object' && 'richText' in value) {
// 富文本,需遍历value.richText
} else if (typeof value === 'object' && 'formula' in value) {
// 公式,value.result才是计算后的值
} else {
// 直接使用value.toString()强制转字符串
const stringValue = String(value);
}
但更推荐使用cell.text,因为它已经帮我们处理了大部分格式问题。
四、完整示例代码
下面是一个完整的函数,读取Excel文件并返回所有单元格的字符串值(保留原样):
const ExcelJS = require('exceljs');
async function readSheetAsStrings(filePath, sheetName) {
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(filePath);
const worksheet = workbook.getWorksheet(sheetName);
const result = [];
worksheet.eachRow((row, rowNumber) => {
const rowData = [];
row.eachCell((cell) => {
rowData.push(cell.text); // 关键:使用text属性
});
result.push(rowData);
});
return result;
}
// 使用示例
(async () => {
const data = await readSheetAsStrings('data.xlsx', 'Sheet1');
console.log(data);
})();
如果你需要处理超大文件,可以结合stream方式逐行读取,但核心逻辑不变。
五、常见误区与最佳实践
-
不要用
cell.value + ''强转:对于空单元格,cell.value为null,会变成字符串"null";日期对象会转成toString()格式,如"Sun Jan 15 2023...",并非所需要的格式。 -
日期处理:若需自定义日期格式,可以先读取
cell.value,再用moment或原生Intl.DateTimeFormat格式化,或者直接使用cell.text。 -
长数字(如身份证号):Excel默认会将超过15位的数字显示为科学计数法,且
cell.text可能返回“1.23457E+17”。最佳实践是在Excel源文件中提前将单元格格式设置为“文本”。若无法修改源文件,可在读取后手动处理:检查字符串长度和是否包含“E+”等。 -
合并单元格:Exceljs中合并单元格区域只有左上角有值,其余单元格
cell.text为空字符串。需通过cell.master或cell.isMerged判断。
六、官方文档与版本说明
该方案基于Exceljs v4.x(当前主流版本)。如果使用旧版(如v3.x),cell.text行为可能略有差异,但基本一致。建议始终使用最新稳定版。
官方文档中关于cell.text的描述:
“Returns the text of the cell. If the cell contains a string, it returns the string. If the cell contains a number, it returns the number formatted using the number format of the cell.”
结语
在Node.js中通过Exceljs读取Excel文件时,永远优先使用cell.text,它能最简洁地获得单元格的“所见即所得”字符串。理解cell.value与cell.text的区别,可以避免90%的读取类型问题。如果你正在构建数据导入、报表生成或ERP系统,请将此技巧加入你的技术备忘录。
希望这篇技术报道能帮助各位开发者少踩一个坑,高效完成Excel数据处理任务。