应用 getCellFormula() 时,Excel 参考中的文件名始终是“[1]”,使用 Apache POI

File name in Excel Reference is always "[1]" using Apache POI when applying getCellFormula()

提问人:Ahmed 提问时间:11/15/2023 最后编辑:Marc Le BihanAhmed 更新时间:11/16/2023 访问量:40

问:

我正在试验并设法使用以下拼切代码 (Java) 读取玩具 Excel 文件:Apache POI 5.2.4

    // .. 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

所有其他字段(如值或字符串)都会正确返回。

我尝试在返回的结构中查找字符串映射表,但找不到任何不为空的内容。如何将此 [1] 解析为引用的 Excel 的原始工作簿名称?它不需要查找或加载它,只需返回文件名即可。currentCell

Java Apache - POI

评论

0赞 Marc Le Bihan 11/15/2023
currentCell.getCellType() == CellType.FORMULA你不愿意测试吗?currentCell.getCellType().equals(CellType.FORMULA)
1赞 Ahmed 11/15/2023
@MarcLeBihan 对于具有相同效果的枚举,我想说更多的是口味问题。感谢您的格式改进!

答:

1赞 Axel Richter 11/15/2023 #1

公式字符串是 Office Open XML 格式 () 的 Excel 在公式中存储指向外部工作簿的链接的方式。因此,您可以获得存储在文件中的内容。[1]Sheet1!$A$3*.xlsx

你可以看到,如果你解压缩文件,看看.在那里你会发现类似的东西:*.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 链接文件,而不仅仅是链接的外部工作簿。因此,正确的方法是从工作簿中获取第一个,然后获取相应的,而不是通过XSSFWorkbook.getExternalLinksTable获取所有内容。externalReferenceExternalLinksTable

如果 .cellXSSFCell

 ... 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);
  while (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();
      String match = matcher.group(1) + matcher.group(2) + matcher.group(3);
      String replacement = matcher.group(1) + referencedFileName + matcher.group(3);
      cellFormula = cellFormula.replace(match, replacement);
     }
    }
   }
  }
  return cellFormula;
 }

评论

0赞 Ahmed 11/15/2023
太棒了,有效!非常感谢您的快速解决方案!