简体   繁体   中英

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

I get an excel file through front end and I do not know what is the user preferred cell reference style (A1 or R1C1) for that file. I want to display the header with column position as present in the file.

For example, if the file is using R1C1 reference style then the column position should be shown as 1, 2, 3... and for A1 references, it should return A, B C...

I want to achieve this using Java apache POI . Any lead in this will be helpful.

Thanks in advance.

The used reference mode (either A1 or R1C1) can be stored in the Excel files. It may be omitted. Then Excel defaults to the last used setting in application.

In the old binary *.xls file system ( HSSF ) it gets stored using a RefModeRecord in the worksheet' s record stream. Although it cannot be different for single worksheets, it will be stored for each worksheet separately. But it cannot be different for different sheets in same workbook.

In Office Open XML file system ( *.xlsx , XSSF ) it gets stored in xl/workbook.xml using element calcPr having attribute refMode set.

Both is not dicrectly suppoerted by apache poi upto now. But if one knows the internally structure of the file systems, then it can be set and get using following code:

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();

 }

}

But question is: Why? Microsoft Excel uses A1 reference mode per default while storing formulas. In stored Excel file systems you never will find R1C1 formulas. Office Open XML stores formulas as strings in XML. And although the Office Open XML specification allows R1C1 there, even Microsoft Excel itself never stores R1C1 formula strings. The old binary *.xls file system stores formulas as binary Ptg records which are independent of their string representation. The conversion to R1C1 is done in Excel GUI only. It is done by the Excel application while parsing the file. Doing this, it puts in memory two kind of formulas each, one A1 and one R1C1. So both kinds of formulas are available in GUI and in VBA.

But apache poi does not support R1C1 formulas until now. If it would must then it would must do the conversion programmatically as the Excel application does. But that code is not public available and not reverse engineered from apache poi up to now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM