如何使用POI库的XSSFReader类读取Excel原始值而非格式化值
获取Excel单元格原始值的解决方案
当使用XSSFReader基于SAX解析Excel时,默认cell方法返回的formattedValue是经过单元格格式设置后的展示值。要获取原始未格式化的数值(比如1.1234),你需要绕过这个方法,直接在SAX解析过程中读取单元格的原始数据:
核心思路
Excel的XLSX格式中,单元格的原始值存储在<v>标签内,而单元格类型由<cell>标签的t属性标识:
- 数值类型(
t="n"):<v>内的内容就是原始数值的字符串形式 - 共享字符串类型(
t="s"):需要通过SharedStringsTable解析对应文本(如果你的场景涉及字符串的话)
实现步骤
- 获取SharedStringsTable(处理字符串单元格时需用到)
SharedStringsTable sst = xssfReader.getSharedStringsTable();
- 自定义SAX ContentHandler
继承DefaultHandler,跟踪当前解析的元素,收集单元格的原始值:
class CustomSheetHandler extends DefaultHandler { private SharedStringsTable sst; private String currentCellRef; private String currentCellType; private StringBuilder cellValueBuilder; private boolean isInVElement; public CustomSheetHandler(SharedStringsTable sst) { this.sst = sst; this.cellValueBuilder = new StringBuilder(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // 记录当前单元格的引用和类型 if ("c".equals(qName)) { currentCellRef = attributes.getValue("r"); currentCellType = attributes.getValue("t"); } // 进入<v>元素时,开始收集原始值 if ("v".equals(qName)) { isInVElement = true; cellValueBuilder.setLength(0); // 清空之前的缓存内容 } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (isInVElement) { cellValueBuilder.append(ch, start, length); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if ("v".equals(qName)) { isInVElement = false; String rawValue = cellValueBuilder.toString(); // 根据单元格类型处理原始值 if ("n".equals(currentCellType)) { // 数值类型,直接转换为原始数值 double originalValue = Double.parseDouble(rawValue); // 调用你的业务处理方法 parseCell(currentCellRef, originalValue, currentRow, currentData, sheetName); } else if ("s".equals(currentCellType)) { // 共享字符串类型,通过SharedStringsTable解析 int idx = Integer.parseInt(rawValue); String originalText = sst.getItemAt(idx).getString(); parseCell(currentCellRef, originalText, currentRow, currentData, sheetName); } else { // 布尔、公式等其他类型按需处理 parseCell(currentCellRef, rawValue, currentRow, currentData, sheetName); } } } // 你的业务处理方法,可根据需求调整参数类型 private void parseCell(String cellRef, Object value, int currentRow, Object currentData, String sheetName) { // 业务逻辑代码 } }
- 替换原有解析逻辑
不再依赖默认的cell方法,使用自定义Handler解析工作表:
XSSFReader.SheetIterator iterator = (XSSFReader.SheetIterator) xssfReader.getSheetsData(); SharedStringsTable sst = xssfReader.getSharedStringsTable(); while (iterator.hasNext()) { InputStream sheetStream = iterator.next(); String sheetName = iterator.getSheetName(); // 初始化SAX解析器并设置自定义Handler XMLReader parser = XMLReaderFactory.createXMLReader(); parser.setContentHandler(new CustomSheetHandler(sst)); parser.parse(new InputSource(sheetStream)); sheetStream.close(); }
说明
- 这种方式直接读取Excel底层存储的原始数据,完全不受单元格格式设置的影响
- 针对不同单元格类型,可以在
endElement方法中扩展对应的处理逻辑 - 如果需要处理公式计算后的原始值,可额外解析
<f>标签结合<v>标签的内容
内容的提问来源于stack exchange,提问作者B RAVI
相关产品推荐
相关产品推荐

