前言
在项目开发中,我们可能需要将数据导出成excel。如果excel的标题都是只占用一个单元格,那可以直接通过某些excel工具类直接生成就行了。但实际情况中表格标题并没有那么简单,可能一个标题有子标题,而子标题里又有子标题,这种情况下就无法使用工具类直接实现了,而手写又太麻烦。这篇文章做的即是直接读取现有的excel表,生成可以生成当前excel表的java代码,以后开发上只需要将生成的java代码贴进自己项目里,便可以直接使用了。
一、引入依赖
本次代码使用到了hutool和poi的依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!-- hutool工具类依赖-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.3</version>
</dependency>
二、读取Excel
代码如下(实例):
String excelFilePath = "现有的excel表格地址";
FileInputStream fis = new FileInputStream(excelFilePath);
Workbook workbook = new HSSFWorkbook(fis);
int headerRowNum = workbook.getSheetAt(0).getLastRowNum();
fis.close();
ExcelReader excelReader = ExcelUtil.getReader(new File(excelFilePath));
List<List<Object>> read = excelReader.read(0, headerRowNum);
excelReader.close();
三、获取标题结构
代码如下(示例):
// 获取标题结构
public static Map<String,List<Integer>> getMap(List<List<Object>> read){
Map<String,List<Integer>> map = new HashMap<>();
int i = 0;
for (List<Object> objects : read) {
int cal = 0;
for (Object object : objects) {
if(map.containsKey(object.toString())){
List<Integer> list = map.get(object.toString());
Integer maxCal = list.get(3);
Integer minCal = list.get(2);
list.remove(3);
list.remove(2);
list.remove(1);
list.add(i);
list.add(minCal);
// 当前列数和该标题所存的列数谁更大,谁大用谁
list.add(Math.max(cal,maxCal));
map.put(object.toString(),list);
}else{
ArrayList<Integer> list = new ArrayList<>();
// 起始行
list.add(i);
// 末尾行
list.add(i);
// 起始列
list.add(cal);
// 末尾列
list.add(cal);
map.put(object.toString(),list);
}
cal++;
}
i++;
}
return map;
}
由于用的是ArrayList,所以增删的操作有点麻烦,如果可以,你可以把ArrayList改成LinkedList。这里的list所存放的内容如注释所表示。 由于这里使用的是HashMap来存储,需要注意两个点:
- 最后遍历map的时候,其顺序并不会跟我们原先excel表里的标题顺序一样,因此需要进行排序。
- map的key不能重复,这也导致表里的标题命名不能重复,不然会重复合并单元格,代码会报错。当然我还没见过标题会重复的excel(hh)。所以应该没多大问题。如果真的有重复的标题,应该就需要采用别的数据结构了。
排序这里我是按每个list的起始列来排序,这样就可以对应现有的excel表了
// 按起始列排序
public static List<Map.Entry<String, List<Integer>>> sortMap(Map<String, List<Integer>> map) {
//利用Map的entrySet方法,转化为list进行排序
List<Map.Entry<String, List<Integer>>> entryList = new ArrayList<>(map.entrySet());
//利用Collections的sort方法对list排序
Collections.sort(entryList, (o1, o2) -> {
//正序排列,倒序反过来
return o1.getValue().get(2) - o2.getValue().get(2);
});
return entryList;
}
四、生成java代码
这里大多都是拼接字符串啦,只要细心点就行了。
// 生成java代码
public static void javaGenator(List<Map.Entry<String, List<Integer>>> stringList,int headerRowNum,Workbook workbook){
Sheet sheet = workbook.getSheetAt(0);
final String excelName = "MyExcelName";
File file = new File("D:\\"+ excelName +".java");
final String excelAddress = "C:\\\\test2.xls"; // 由于\\字符串会被识别为\,因此这里需要写四个\
try (FileWriter fw = new FileWriter(file); PrintWriter pw = new PrintWriter(fw)) {
pw.println("import cn.hutool.poi.excel.ExcelUtil;");
pw.println("import cn.hutool.poi.excel.ExcelWriter;");
pw.println("public class " + excelName +"{");
pw.print(" ");
pw.println("public static void main(String[] args){");
pw.print(" ");
pw.println("ExcelWriter writer = ExcelUtil.getWriter(\""+ excelAddress +"\");");
for (Map.Entry<String, List<Integer>> listEntry : stringList) {
String name = listEntry.getKey();
List<Integer> list = listEntry.getValue();
int minRow = list.get(0);
int maxRow = list.get(1);
int minCal = list.get(2);
int maxCal = list.get(3);
pw.print(" ");
pw.println("writer.setColumnWidth(" + minCal + ", "+ sheet.getColumnWidth(minCal)/100 +");");
pw.print(" ");
pw.println("writer.merge("+ minRow +", " + maxRow + ", " + minCal + ", " + maxCal +", \""+ name +"\", false);");
}
for (int j = 0; j <= headerRowNum; j++) {
pw.print(" ");
pw.println("writer.passCurrentRow();");
}
pw.print(" ");
pw.println("writer.flush();");
pw.print(" ");
pw.println("writer.close();");
pw.print(" ");
pw.println("}");
pw.println("}");
}catch (Exception e){
e.printStackTrace();
}
}
五、总结
完整代码
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.*;
import java.util.*;
/**
* @author Namego
* @date 2023/4/16 21:44
*/
public class MyExcelUtil {
public static void main(String[] args) throws IOException {
String excelFilePath = "C:\\response2.xls";
FileInputStream fis = new FileInputStream(excelFilePath);
Workbook workbook = new HSSFWorkbook(fis);
int headerRowNum = workbook.getSheetAt(0).getLastRowNum();
fis.close();
ExcelReader excelReader = ExcelUtil.getReader(new File(excelFilePath));
List<List<Object>> read = excelReader.read(0, headerRowNum);
excelReader.close();
Map<String,List<Integer>> map = getMap(read);
List<Map.Entry<String, List<Integer>>> stringList = sortMap(map);
javaGenator(stringList,headerRowNum,workbook);
workbook.close();
}
// 生成java代码
public static void javaGenator(List<Map.Entry<String, List<Integer>>> stringList,int headerRowNum,Workbook workbook){
Sheet sheet = workbook.getSheetAt(0);
final String excelName = "MyExcelName";
File file = new File("D:\\"+ excelName +".java");
final String excelAddress = "C:\\\\test2.xls"; // 由于\\字符串会被识别为\,因此这里需要写四个\
try (FileWriter fw = new FileWriter(file); PrintWriter pw = new PrintWriter(fw)) {
pw.println("import cn.hutool.poi.excel.ExcelUtil;");
pw.println("import cn.hutool.poi.excel.ExcelWriter;");
pw.println("public class " + excelName +"{");
pw.print(" ");
pw.println("public static void main(String[] args){");
pw.print(" ");
pw.println("ExcelWriter writer = ExcelUtil.getWriter(\""+ excelAddress +"\");");
for (Map.Entry<String, List<Integer>> listEntry : stringList) {
String name = listEntry.getKey();
List<Integer> list = listEntry.getValue();
int minRow = list.get(0);
int maxRow = list.get(1);
int minCal = list.get(2);
int maxCal = list.get(3);
pw.print(" ");
pw.println("writer.setColumnWidth(" + minCal + ", "+ sheet.getColumnWidth(minCal)/100 +");");
pw.print(" ");
pw.println("writer.merge("+ minRow +", " + maxRow + ", " + minCal + ", " + maxCal +", \""+ name +"\", false);");
}
for (int j = 0; j <= headerRowNum; j++) {
pw.print(" ");
pw.println("writer.passCurrentRow();");
}
pw.print(" ");
pw.println("writer.flush();");
pw.print(" ");
pw.println("writer.close();");
pw.print(" ");
pw.println("}");
pw.println("}");
}catch (Exception e){
e.printStackTrace();
}
}
// 获取标题结构
public static Map<String,List<Integer>> getMap(List<List<Object>> read){
Map<String,List<Integer>> map = new HashMap<>();
int i = 0;
for (List<Object> objects : read) {
int cal = 0;
for (Object object : objects) {
if(map.containsKey(object.toString())){
List<Integer> list = map.get(object.toString());
Integer maxCal = list.get(3);
Integer minCal = list.get(2);
list.remove(3);
list.remove(2);
list.remove(1);
list.add(i);
list.add(minCal);
list.add(Math.max(cal,maxCal));
map.put(object.toString(),list);
}else{
ArrayList<Integer> list = new ArrayList<>();
// 起始行
list.add(i);
// 末尾行
list.add(i);
// 起始列
list.add(cal);
// 末尾列
list.add(cal);
map.put(object.toString(),list);
}
cal++;
}
i++;
}
return map;
}
// 按起始列排序
public static List<Map.Entry<String, List<Integer>>> sortMap(Map<String, List<Integer>> map) {
//利用Map的entrySet方法,转化为list进行排序
List<Map.Entry<String, List<Integer>>> entryList = new ArrayList<>(map.entrySet());
//利用Collections的sort方法对list排序
Collections.sort(entryList, (o1, o2) -> {
//正序排列,倒序反过来
return o1.getValue().get(2) - o2.getValue().get(2);
});
return entryList;
}
}
由于是用main函数来测试的,所以方法都是静态,可以根据你实际情况修改