簡體   English   中英

如何使用 apache POI 檢測文件的 excel 單元格引用樣式?

[英]How to detect excel cell reference style of a file using apache POI?

我通過前端獲得了一個 excel 文件,但我不知道該文件的用戶首選單元格引用樣式(A1 或 R1C1)是什么。 我想在文件中顯示 header 列 position 。

例如,如果文件使用 R1C1 引用樣式,則列 position 應顯示為 1、2、3...,對於 A1 引用,它應返回 A、B C...

我想使用Java apache POI來實現這一點。 這方面的任何線索都會有所幫助。

提前致謝。

使用的參考模式(A1 或 R1C1)可以存儲在 Excel 文件中。 可以省略。 然后 Excel 默認為應用程序中最后使用的設置。

在舊的二進制*.xls文件系統 ( HSSF ) 中,它使用RefModeRecord存儲在工作表的記錄 stream 中。 盡管對於單個工作表不能不同,但它將為每個工作表單獨存儲。 但同一工作簿中的不同工作表不能不同。

在 Office Open XML 文件系統( *.xlsxXSSF )中,它使用設置了屬性refMode的元素calcPr存儲在xl/workbook.xml中。

到目前為止,兩者都沒有被apache poi直接支持。 但是,如果知道文件系統的內部結構,則可以使用以下代碼進行設置和獲取:

import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.record.RecordBase;
import org.apache.poi.hssf.record.RefModeRecord;
import org.apache.poi.hssf.model.InternalSheet;

import java.lang.reflect.Field;
import java.util.List;

public class CreateExcelRefModes {
    
 static void setRefMode(HSSFWorkbook hssfWorkbook, String refMode) throws Exception {
  for (Sheet sheet : hssfWorkbook) {
   HSSFSheet hssfSheet = (HSSFSheet)sheet;
   
   Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
   _sheet.setAccessible(true); 
   InternalSheet internalsheet = (InternalSheet)_sheet.get(hssfSheet); 

   Field _records = InternalSheet.class.getDeclaredField("_records");
   _records.setAccessible(true);
   @SuppressWarnings("unchecked") 
   List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
  
   RefModeRecord refModeRecord = null;
   for (RecordBase record : records) {
    if (record instanceof RefModeRecord) refModeRecord = (RefModeRecord)record;
   }
   if ("R1C1".equals(refMode)) {
    if (refModeRecord == null) {
     refModeRecord = new RefModeRecord();
     records.add(records.size() - 1, refModeRecord);
    }
    refModeRecord.setMode(RefModeRecord.USE_R1C1_MODE);
   } else if ("A1".equals(refMode)) {
    if (refModeRecord == null) {
     refModeRecord = new RefModeRecord();
     records.add(records.size() - 1, refModeRecord);
    }
    refModeRecord.setMode(RefModeRecord.USE_A1_MODE);
   }   
  }  
 }
 
 static String getRefMode(HSSFWorkbook hssfWorkbook) throws Exception  {
  for (Sheet sheet : hssfWorkbook) {
   HSSFSheet hssfSheet = (HSSFSheet)sheet;
   
   Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
   _sheet.setAccessible(true); 
   InternalSheet internalsheet = (InternalSheet)_sheet.get(hssfSheet); 

   Field _records = InternalSheet.class.getDeclaredField("_records");
   _records.setAccessible(true);
   @SuppressWarnings("unchecked") 
   List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
  
   RefModeRecord refModeRecord = null;
   for (RecordBase record : records) {
    if (record instanceof RefModeRecord) refModeRecord = (RefModeRecord)record;
   }
   if (refModeRecord == null) return "not specified";
   if (refModeRecord.getMode() == RefModeRecord.USE_R1C1_MODE) return "R1C1";
   if (refModeRecord.getMode() == RefModeRecord.USE_A1_MODE) return "A1";
  }
  return null;
 }
 
 static void setRefMode(XSSFWorkbook xssfWorkbook, String refMode) {
  if ("R1C1".equals(refMode)) {
   if (xssfWorkbook.getCTWorkbook().getCalcPr() == null) xssfWorkbook.getCTWorkbook().addNewCalcPr();
   xssfWorkbook.getCTWorkbook().getCalcPr().setRefMode(org.openxmlformats.schemas.spreadsheetml.x2006.main.STRefMode.R_1_C_1);
  } else if ("A1".equals(refMode)) {
   if (xssfWorkbook.getCTWorkbook().getCalcPr() == null) xssfWorkbook.getCTWorkbook().addNewCalcPr();
   xssfWorkbook.getCTWorkbook().getCalcPr().setRefMode(org.openxmlformats.schemas.spreadsheetml.x2006.main.STRefMode.A_1);    
  }
 }
 
 static String getRefMode(XSSFWorkbook xssfWorkbook) {
  if (xssfWorkbook.getCTWorkbook().getCalcPr() == null) return "not specified";
  if (xssfWorkbook.getCTWorkbook().getCalcPr().getRefMode() == org.openxmlformats.schemas.spreadsheetml.x2006.main.STRefMode.R_1_C_1) return "R1C1";
  if (xssfWorkbook.getCTWorkbook().getCalcPr().getRefMode() == org.openxmlformats.schemas.spreadsheetml.x2006.main.STRefMode.A_1) return "A1";
  return null;
 }


 public static void main(String[] args) throws Exception {
     
  Workbook workbook = new XSSFWorkbook(); String filePath = "./CreateExcelRefModes.xlsx";
  //Workbook workbook = new HSSFWorkbook(); String filePath = "./CreateExcelRefModes.xls";

  Sheet sheet = workbook.createSheet();

  if (workbook instanceof XSSFWorkbook) {
   XSSFWorkbook xssfWorkbook = (XSSFWorkbook)workbook;   
   setRefMode(xssfWorkbook, "R1C1" );
   //setRefMode(xssfWorkbook, "A1" );
   System.out.println(getRefMode(xssfWorkbook));
  } else if (workbook instanceof HSSFWorkbook) {
   HSSFWorkbook hssfWorkbook = (HSSFWorkbook)workbook;
   setRefMode(hssfWorkbook, "R1C1" );
   //setRefMode(hssfWorkbook, "A1" );
   System.out.println(getRefMode(hssfWorkbook));
  }

  FileOutputStream out = new FileOutputStream(filePath);
  workbook.write(out);
  out.close();
  workbook.close();

 }

}

但問題是:為什么? Microsoft Excel 在存儲公式時默認使用 A1 參考模式。 在存儲的 Excel 文件系統中,您永遠不會找到 R1C1 公式。 Office Open XML 將公式作為字符串存儲在 XML 中。 盡管 Office Open XML 規范允許 R1C1 存在,但即使是 Microsoft Excel 本身也從不存儲 R1C1 公式字符串。 舊的二進制*.xls文件系統將公式存儲為獨立於其字符串表示的二進制Ptg記錄。 僅在 Excel GUI 中完成到 R1C1 的轉換。 它由 Excel 應用程序在解析文件時完成。 這樣做,它會在 memory 中輸入兩種公式,一種是 A1,一種是 R1C1。 所以這兩種公式都可以在 GUI 和 VBA 中使用。

但是apache poi直到現在還不支持 R1C1 公式。 如果必須,則必須像 Excel 應用程序那樣以編程方式進行轉換。 但到目前為止,該代碼尚未公開,也未從apache poi進行逆向工程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM