在使用 EasyExcel 读取 Excel 时,我们经常会遇到这样一个问题:
同一个单元格,Excel 界面显示 119178409
,但在公式栏里显示的原始值却是 119178408.985065
。
而通过 EasyExcel 读取后,拿到的值始终是 119178409,即 Excel 的“显示值”,而不是底层存储的原始值。
这在财务、对账、数据精度要求较高的场景里,往往会造成严重的数据误差。
Excel 单元格内容:
119178409
119178408.985065
使用 EasyExcel 默认读取方式:
EasyExcel.read(fileBytes)
.registerReadListener(new ReadListener<Map<Integer, String>>() {
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
System.out.println(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}).sheet().doRead();
输出结果:
{0=119178409}
可以看到,原始的 119178408.985065
被四舍五入成了 119178409
。
EasyExcel 的默认行为是:
Map<Integer, String>
,使用内置的 StringConverter
。StringConverter
内部依赖 Apache POI 的 DataFormatter
。DataFormatter
返回的就是 Excel UI 显示值,而不是单元格底层的存储值。所以,只要你用 Map<Integer, String>
或者 Java Bean 的 String
字段接收,就一定会丢失原始精度。
Map<Integer, CellData>
(推荐)直接拿 CellData
,通过 getNumberValue()
获取 Excel 的原始值:
EasyExcel.read(fileBytes)
.registerReadListener(new ReadListener<Map<Integer, CellData>>() {
@Override
public void invoke(Map<Integer, CellData> data, AnalysisContext context) {
for (Map.Entry<Integer, CellData> entry : data.entrySet()) {
CellData cellData = entry.getValue();
if (cellData.getType() == CellDataTypeEnum.NUMBER) {
System.out.println("原始值 = " + cellData.getNumberValue().toPlainString());
}
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}).sheet().doRead();
输出结果:
原始值 = 119178408.985065
如果你必须用 Map<Integer, String>
,可以自定义一个 RawNumberStringConverter
:
public class RawNumberStringConverter implements Converter<String> {
@Override
public Class<?> supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return cellData.getNumberValue().toPlainString();
}
}
注册时一定要放在前面,覆盖默认的:
EasyExcel.read(fileBytes)
.registerConverter(new RawNumberStringConverter())
.registerReadListener(new ReadListener<Map<Integer, String>>() {
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
System.out.println(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}).sheet().doRead();
这样 data
中拿到的就是 119178408.985065
。
Map<Integer, CellData>
,直接取 getNumberValue()
;Converter
覆盖默认的 StringConverter
。⚠️ 经验教训:EasyExcel 在默认场景下非常方便,但在高精度数值处理场景里,一定要检查它是否返回了 原始值,否则可能踩坑。