EasyExcel 是基于 Apache POI 的封裝,主要專注于簡化 Excel 的讀寫操作,對于公式計算的支持相對有限。以下是 EasyExcel 中處理公式計算的全面指南:
1. 基本公式寫入
1.1 寫入簡單公式
@Data
public class FormulaData {@ExcelProperty("數值1")private Double value1;@ExcelProperty("數值2")private Double value2;@ExcelProperty("求和")private String sumFormula;@ExcelProperty("平均值")private String avgFormula;
}// 寫入公式
List<FormulaData> list = new ArrayList<>();
FormulaData data = new FormulaData();
data.setValue1(10.0);
data.setValue2(20.0);
data.setSumFormula("SUM(A2,B2)"); // 引用當前行的單元格
data.setAvgFormula("AVERAGE(A2,B2)");
list.add(data);EasyExcel.write("formula_example.xlsx", FormulaData.class).sheet("公式示例").doWrite(list);
1.2 使用絕對引用
data.setSumFormula("SUM($A$2:$B$2)"); // 絕對引用
2. 讀取包含公式的Excel
2.1 基本讀取
// 監聽器實現
public class FormulaDataListener extends AnalysisEventListener<FormulaData> {@Overridepublic void invoke(FormulaData data, AnalysisContext context) {// 這里獲取的是公式計算后的值System.out.println("求和結果: " + data.getSumFormula());}@Overridepublic void doAfterAllAnalysed(ActionContext context) {}
}// 讀取文件
EasyExcel.read("formula_example.xlsx", FormulaData.class, new FormulaDataListener()).sheet().doRead();
2.2 獲取公式本身(而非計算結果)
public class FormulaReadListener extends AnalysisEventListener<Map<Integer, String>> {@Overridepublic void invoke(Map<Integer, String> data, AnalysisContext context) {// 獲取底層CellReadCellData<?> cellData = (ReadCellData<?>) data.get(2); // 第三列if (cellData.getFormulaData() != null) {System.out.println("公式內容: " + cellData.getFormulaData());}}
}EasyExcel.read("formula_example.xlsx", new FormulaReadListener()).sheet().doRead();
3. 動態公式設置
3.1 使用攔截器動態設置公式
public class FormulaWriteHandler implements CellWriteHandler {@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {if (!isHead && head.getFieldName().equals("sumFormula")) {// 設置求和公式,如SUM(A2,B2)int rowNum = cell.getRowIndex() + 1; // Excel行號從1開始String formula = String.format("SUM(A%d,B%d)", rowNum, rowNum);cell.setCellFormula(formula);}}
}// 使用
EasyExcel.write("dynamic_formula.xlsx", FormulaData.class).registerWriteHandler(new FormulaWriteHandler()).sheet().doWrite(dataList);
3.2 復雜公式示例
// IF函數示例
String ifFormula = "IF(A2>B2, \"A大于B\", IF(A2<B2, \"A小于B\", \"A等于B\"))";// VLOOKUP函數示例
String vlookupFormula = "VLOOKUP(D2,$A$2:$B$10,2,FALSE)";// 日期函數
String dateFormula = "TEXT(TODAY(),\"yyyy-mm-dd\")";
4. 公式計算控制
4.1 強制重新計算公式
// 寫入后強制計算
ExcelWriter excelWriter = EasyExcel.write("output.xlsx", FormulaData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
excelWriter.write(dataList, writeSheet);// 獲取底層Workbook并強制重新計算
SXSSFWorkbook workbook = (SXSSFWorkbook) excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
workbook.getCreationHelper().createFormulaEvaluator().evaluateAll();excelWriter.finish();
4.2 設置不計算公式(僅保留公式)
// 在讀取時不計算公式
EasyExcel.read("input.xlsx", new FormulaReadListener()).ignoreEmptyRow(false).autoTrim(true).formulaResult(false) // 不計算公式結果.sheet().doRead();
5. 常見函數示例
5.1 數學函數
// 基本數學運算
String addFormula = "A2+B2";
String multiplyFormula = "PRODUCT(A2,B2)";
String roundFormula = "ROUND(A2,2)";// 條件求和
String sumifFormula = "SUMIF(A2:A10,\">10\",B2:B10)";
5.2 文本函數
// 連接文本
String concatFormula = "CONCATENATE(A2,\"-\",B2)";// 提取子串
String leftFormula = "LEFT(A2,3)";
String midFormula = "MID(A2,2,3)";
5.3 日期函數
// 計算日期差
String dateDiffFormula = "DATEDIF(A2,B2,\"d\")";// 添加天數
String addDaysFormula = "A2+7"; // A2是日期單元格
6. 高級技巧
6.1 跨工作表公式
// 引用其他工作表的單元格
String crossSheetFormula = "SUM(Sheet2!A1:A10)";// 引用其他工作簿(需要文件在同一目錄)
String externalRefFormula = "'[OtherWorkbook.xlsx]Sheet1'!A1";
6.2 數組公式
// 數組公式需要用大括號包圍
String arrayFormula = "{SUM(A2:A10*B2:B10)}";// 寫入時需要特殊處理
cell.setCellFormula("SUM(A2:A10*B2:B10)");
// 然后需要將單元格標記為數組公式區域
CellRangeAddress range = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(),cell.getColumnIndex(), cell.getColumnIndex());
sheet.addMergedRegion(range);
sheet.setArrayFormula(cell.getCellFormula(), range);
6.3 命名范圍公式
// 創建命名范圍
Name namedRange = workbook.createName();
namedRange.setNameName("SalesData");
namedRange.setRefersToFormula("Sheet1!$A$2:$A$10");// 使用命名范圍
String namedRangeFormula = "SUM(SalesData)";
// 1. 定義數據類
@Data
public class FinancialData {@ExcelProperty("月份")private String month;@ExcelProperty("收入")private Double income;@ExcelProperty("支出")private Double expense;@ExcelProperty("利潤")private String profitFormula;@ExcelProperty("利潤率")private String marginFormula;
}// 2. 公式寫入處理器
public class FinancialFormulaHandler implements CellWriteHandler {@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {if (!isHead) {int rowNum = cell.getRowIndex() + 1;String column = head.getFieldName();if ("profitFormula".equals(column)) {cell.setCellFormula(String.format("B%d-C%d", rowNum, rowNum));} else if ("marginFormula".equals(column)) {cell.setCellFormula(String.format("(B%d-C%d)/B%d", rowNum, rowNum, rowNum));// 設置百分比格式CellStyle style = writeSheetHolder.getSheet().getWorkbook().createCellStyle();style.setDataFormat((short)0xa); // 百分比格式cell.setCellStyle(style);}}}
}// 3. 使用示例
public void exportFinancialReport() {List<FinancialData> dataList = new ArrayList<>();// 添加數據...ExcelWriter excelWriter = EasyExcel.write("financial_report.xlsx", FinancialData.class).registerWriteHandler(new FinancialFormulaHandler()).build();WriteSheet writeSheet = EasyExcel.writerSheet("財務報告").build();excelWriter.write(dataList, writeSheet);// 強制計算公式SXSSFWorkbook workbook = (SXSSFWorkbook) excelWriter.writeContext().writeWorkbookHolder().getWorkbook();workbook.getCreationHelper().createFormulaEvaluator().evaluateAll();excelWriter.finish();
}