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

问题描述 投票:0回答:1

我正在尝试

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 的原始工作簿名称?它不需要查找或加载它,只需返回文件名即可。

java apache-poi
1个回答
0
投票

公式字符串

[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;
 }
© www.soinside.com 2019 - 2024. All rights reserved.