我正在尝试
Apache POI 5.2.4
并设法使用以下剪切代码(Java)读取玩具 Excel 文件:
// .. get file
FileInputStream excelFile = new FileInputStream(file);
Workbook workbook = new XSSFWorkbook(excelFile);
Sheet datatypeSheet = workbook.getSheetAt(0);
Iterator<Row> iterator = datatypeSheet.iterator();
while (iterator.hasNext()) {
Row currentRow = iterator.next();
Iterator<Cell> cellIterator = currentRow.iterator();
while (cellIterator.hasNext()) {
Cell currentCell = cellIterator.next();
if (currentCell.getCellType() == CellType.FORMULA) {
// !!! Interesting part !!!
String cellLabel = currentCell.getCellFormula();
// Here I get values like "[1]Sheet1!$A$3"
// instead of "[Book1.xlsx]Sheet1!$A$3"
// !!! End interesting part !!!
}
}
}
我读入的 Excel 工作表仅包含一个引用同一文件夹中另一个工作簿的单元格。它是使用 Excel 2019 for Mac 创建的,并且正确显示链接值。
而不是我放入 Excel 的文件名
[Book1.xlsx]Sheet1!$A$3
我只得到
[1]Sheet1!$A$3
。
所有其他字段(例如值或字符串)都会正确返回。
我尝试在返回的结构中查找
currentCell
的字符串映射表,但找不到任何不为空的内容。如何将此 [1] 解析为引用的 Excel 的原始工作簿名称?它不需要查找或加载它,只需返回文件名即可。
公式字符串
[1]Sheet1!$A$3
是 Office Open XML 格式 (*.xlsx
) 的 Excel 在公式中存储指向外部工作簿的链接的方式。这样您就可以获得文件中存储的内容。
您可以看到,如果您解压
*.xlsx
文件并查看 /xl/worksheets/sheet*.xml
。在那里你会发现类似的东西:
...
<row ...>
<c ...>
<f>[1]Sheet1!$A$3</f>
<v>...</v>
</c>
...
</row>
...
这可能是为了避免公式字符串太长。
括号内的索引指的是存储在
/xl/workbook.xml
中的外部参考:
...
<externalReferences>
<externalReference r:id="rId2"/>
<externalReference r:id="rId3"/>
</externalReferences>
...
r:id
指的是/xl/workbook.xml.rels
中的外部链接。
这会引用
/xl/externalLinks/externalLink*.xml
中的外部链接。
然后,此外部链接使用
/xl/externalLinks/_rels/externalLink*.xml.rels
引用外部工作簿。
Apache POI 提供 org.apache.poi.xssf.model.ExternalLinksTable。但它也可能包含 DDE 和/或 OLE 链接文件,而不仅仅是链接的外部 wokbook。所以正确的方法是先从wrkbook中获取
externalReference
,然后获取对应的ExternalLinksTable
,而不是通过XSSFWorkbook.getExternalLinksTable获取全部。
如果
cell
是 XSSFCell
,以下方法应该可以实现你想要的。
... String getCellFormula(XSSFCell cell) {
String cellFormula = cell.getCellFormula();
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(\\[)(\\d*)(\\])");
java.util.regex.Matcher matcher = pattern.matcher(cellFormula);
if (matcher.find()) {
String externalWorkbookIndex = matcher.group(2); // 1 based : [1] is first
int externalReferenceIdx = Integer.valueOf(externalWorkbookIndex) - 1; // 0 based : 0 is first
XSSFWorkbook workbook = cell.getSheet().getWorkbook();
if (workbook.getCTWorkbook().getExternalReferences() != null) {
if (workbook.getCTWorkbook().getExternalReferences().getExternalReferenceList().size() > externalReferenceIdx) {
String rId = workbook.getCTWorkbook().getExternalReferences().getExternalReferenceList().get(externalReferenceIdx).getId();
if (workbook.getRelationById(rId) instanceof org.apache.poi.xssf.model.ExternalLinksTable) {
org.apache.poi.xssf.model.ExternalLinksTable externalLinksTable =
(org.apache.poi.xssf.model.ExternalLinksTable)workbook.getRelationById(rId);
String referencedFileName = externalLinksTable.getLinkedFileName();
cellFormula = matcher.replaceFirst("$1" + referencedFileName + "$3");
}
}
}
}
return cellFormula;
}