【解析excel】利用easyexcel解析excel
【解析excel】利用easyexcel解析excel
- POM
- 监听类
- 工具类
- 测试类
- 部分测试结果
- 备注
- 其他
EasyExcel
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便
POM
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel-core</artifactId><version>3.2.1</version><exclusions><exclusion><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId></exclusion><exclusion><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId></exclusion><exclusion><groupId>org.apache.poi</groupId><artifactId>poi</artifactId></exclusion><exclusion><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId></exclusion></exclusions></dependency>
监听类
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Cell;
import com.alibaba.excel.metadata.data.ReadCellData;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;public class MyExcelListener extends AnalysisEventListener<Map<Integer, String>> {private Map<Integer, String> headMap = new HashMap<>();private List<Map<Integer, String>> valList = new ArrayList<>();private Map<String, String> headValAndTypeMap = new HashMap<>();private List<Map<String, String>> contentValAndTypeList = new ArrayList<>();private boolean nameAndTypeFlag;private Integer startColumnNum;private Integer startRowNum;private Integer endColumnNum;private Integer endRowNum;// sheet中最大的列号,默认为0,不可为nullprivate int maxColumnNum;// sheet中行数,默认为1,因为从第二行开始读取private int maxRowNum = 1;@Overridepublic void invoke(Map<Integer, String> integerStringMap, AnalysisContext analysisContext) {if (integerStringMap == null || integerStringMap.size()==0){return;}// 获取当前行数analysisContext.readRowHolder().getRowIndex() 从1开始(0行进的head方法)Integer currentRowNum = analysisContext.readRowHolder().getRowIndex();if(currentRowNum<startRowNum-1){return;}// 扫描excel全部内容获取此excel最大列数maxColumnNum = Math.max(maxColumnNum,integerStringMap.size());// 最大行数maxRowNum++;// 起始列,Map中小于startColumnNum-1都不需要integerStringMap.entrySet().removeIf(entry -> entry.getKey() < startColumnNum-1);// 移除值为null的数据integerStringMap.entrySet().removeIf(entry -> entry.getValue() == null);// 格式化单元格中的数据formatExcelValByCellType(integerStringMap,analysisContext.readRowHolder().getCellMap());// 本方法从excel第二行开始读,为防止起始不是excel第一行,多读入一行数据if (nameAndTypeFlag && (contentValAndTypeList.size() == 0 || contentValAndTypeList.size() == 1)){// 如果获取名称和类型,只获取一行数据contentValAndTypeList.add(getCellType(integerStringMap,analysisContext.readRowHolder().getCellMap()));} else if (endRowNum == null){// 未设置结束单元格无结束行数,则读取全部valList.add(integerStringMap);} else if (valList.size() < endRowNum-1){valList.add(integerStringMap);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {Set<Map.Entry<Integer, ReadCellData<?>>> entrieSet = headMap.entrySet();for (Map.Entry<Integer, ReadCellData<?>> entry : entrieSet) {String val = entry.getValue().getType()== CellDataTypeEnum.EMPTY?"":entry.getValue().getStringValue();this.headMap.put(entry.getKey(),val);}if (startRowNum==1 && nameAndTypeFlag && headValAndTypeMap.size() == 0){// 如果获取名称和类型,只获取一行数据headValAndTypeMap = getCellType(this.headMap,context.readRowHolder().getCellMap());}}private void formatExcelValByCellType(Map<Integer, String> integerStringMap, Map<Integer, Cell> cellMap){for (Integer key : integerStringMap.keySet()) {ReadCellData cell = (ReadCellData) cellMap.get(key);String newVal = MyExcelUtils.getOtherDateFormat(cell,cell.getDataFormatData().getFormat());if (newVal!=null && !"".equals(newVal)){integerStringMap.put(key,newVal);}}}/*** eg: 0:张三 0_type:String* @param integerStringMap* @param cellMap* @return*/private Map<String, String> getCellType(Map<Integer, String> integerStringMap, Map<Integer, Cell> cellMap){Map<String, String> nameAndTypeMap = new HashMap<>();// key取值是 0 1 2 3....for (Integer key : integerStringMap.keySet()) {nameAndTypeMap.put(String.valueOf(key),integerStringMap.get(key));ReadCellData cell = (ReadCellData) cellMap.get(key);String cellType = MyExcelUtils.getCellType(cell,integerStringMap.get(key));if (cellType!=null && !"".equals(cellType)){nameAndTypeMap.put(key+"_type",cellType);}}return nameAndTypeMap;}public Map<Integer, String> getHeadMap() {return headMap;}public void setHeadMap(Map<Integer, String> headMap) {this.headMap = headMap;}public List<Map<Integer, String>> getValList() {return valList;}public void setValList(List<Map<Integer, String>> valList) {this.valList = valList;}public Integer getStartColumnNum() {return startColumnNum;}public void setStartColumnNum(Integer startColumnNum) {this.startColumnNum = startColumnNum;}public Integer getStartRowNum() {return startRowNum;}public void setStartRowNum(Integer startRowNum) {this.startRowNum = startRowNum;}public Integer getEndColumnNum() {return endColumnNum;}public void setEndColumnNum(Integer endColumnNum) {this.endColumnNum = endColumnNum;}public Integer getEndRowNum() {return endRowNum;}public void setEndRowNum(Integer endRowNum) {this.endRowNum = endRowNum;}public int getMaxColumnNum() {return maxColumnNum;}public void setMaxColumnNum(int maxColumnNum) {this.maxColumnNum = maxColumnNum;}public int getMaxRowNum() {return maxRowNum;}public void setMaxRowNum(int maxRowNum) {this.maxRowNum = maxRowNum;}public boolean isNameAndTypeFlag() {return nameAndTypeFlag;}public void setNameAndTypeFlag(boolean nameAndTypeFlag) {this.nameAndTypeFlag = nameAndTypeFlag;}public Map<String, String> getHeadValAndTypeMap() {return headValAndTypeMap;}public void setHeadValAndTypeMap(Map<String, String> headValAndTypeMap) {this.headValAndTypeMap = headValAndTypeMap;}public List<Map<String, String>> getContentValAndTypeList() {return contentValAndTypeList;}public void setContentValAndTypeList(List<Map<String, String>> contentValAndTypeList) {this.contentValAndTypeList = contentValAndTypeList;}
}
工具类
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.DateUtils;
import org.apache.commons.lang.time.DateFormatUtils;import java.io.DataInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import static com.nature.ysl.zl.excel.MyExcelUtils.SPECTYPESIGN;public class MyExcelUtils {public static final String SPECTYPESIGN = "#@#";// 字母private static final String MATCH_LETTERS = "[a-zA-Z]+";// 匹配字母开头private static final String LETTER_START_REGEX = "^[a-zA-Z]*$";// 提取单元格的行数private static final String ROW_REGEX = "[\\d]+";// 匹配中文private static final String CHINESETYPE_REGEX = ".*[\\u4e00-\\u9fa5].*";// 单元格数值类型private static final String NUMBERTYPE_REGEX = "[0|.]+";// 匹配除了,.数字之前外的所有符号,0_ 代表整数private static final String EXCLUDE_SPECIFIC_REGEX = ".*[^,.#\\d].*";private static final String CELL_REGEX = "[A-Z]+[1-9][\\d]*";private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]");private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]");private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]");/*** 获取excel文件所有的sheet页名称** @param ins* @return*/public static List<String> getSheetList(InputStream ins) {List<String> resList = new ArrayList<>();ExcelReaderBuilder excelReaderBuilder = EasyExcel.read(ins);ExcelReader excelReader = excelReaderBuilder.build();List<ReadSheet> sheetList = excelReader.excelExecutor().sheetList();sheetList.forEach(e -> resList.add(e.getSheetName()));return resList;}/*** 获取sheet种的内容,前rowNum行,null则查询全部* @param ins* @param sheetName* @param rowNum* @return*/public static List<List<String>> getSheetData(InputStream ins, String sheetName, Integer rowNum) {if (rowNum != null && rowNum == 0){return null;}InputStream is = null;is = new DataInputStream(ins);MyExcelListener listen = new MyExcelListener();// 设置读取的行数listen.setEndRowNum(rowNum);ExcelReaderBuilder read = EasyExcel.read(is, listen);read.sheet(sheetName).doRead();// 读取sheet中最大的列数int maxColumnNum = listen.getMaxColumnNum();// 第一行数据(也就是表头数据),下标从0开始Map<Integer, String> headMap = listen.getHeadMap();// 其余数据,下标从0开始List<Map<Integer, String>> valList = listen.getValList();// 为还原excel原始样式,以最大列数为约束,遍历headMap、valList,获取不到的数据以空填充// 如果rowNum不为空,开始填充数据rowNum = rowNum == null?listen.getMaxRowNum():rowNum;List<List<String>> resList = new ArrayList();for (int i = 0; i < rowNum; i++) {List<String> list = new ArrayList<>();for (int j = 0; j < maxColumnNum; j++) {if (i == 0){// 如果不存在默认返回空list.add(headMap.getOrDefault(j,""));}else {list.add(valList.get(i-1).getOrDefault(j,""));}}resList.add(list);}return resList;}/*** 部分格式数据处理* @param cell* @param formatVal* @return*/public static String getOtherDateFormat(ReadCellData cell, String formatVal){String newFormatStr = MyExcelTypeEnum.getFormatType(formatVal);if (cell.getDataFormatData().getIndex() == 22) {// excel显示格式为:2012/1/20 23:00return DateFormatUtils.format(doubleToDate(cell.getNumberValue().doubleValue()), "yyyy/M/d H:mm");} else if (cell.getDataFormatData().getIndex() == 30) {return DateFormatUtils.format(doubleToDate(cell.getNumberValue().doubleValue()), "M/d/yy");}if (StringUtils.isEmpty(newFormatStr)){return null;}String dateStr = DateFormatUtils.format(doubleToDate(cell.getNumberValue().doubleValue()),newFormatStr);if (StringUtils.contains(dateStr,SPECTYPESIGN)){// 二〇〇五年一月十五日return convertNumberToChineseDate(dateStr,SPECTYPESIGN);} else if (StringUtils.contains(newFormatStr," aa")) {// 2011/1/3 6:00 AMreturn DateFormatUtils.format(doubleToDate(cell.getNumberValue().doubleValue()), newFormatStr, Locale.ENGLISH);} else if (StringUtils.contains(newFormatStr,"MMM")) {// J 、J-23return getEnglishDate(cell.getNumberValue().doubleValue(),newFormatStr);}return dateStr;}/*** 将yyyy年-MM月-dd日 格式日期转换成中文格式* 例:2000-1-1 --> 二〇〇〇年一月一日*/public static String convertNumberToChineseDate(String date,String splitStr) {if (date == null || "".equals(date)) {return date;}try {String[] dates = date.split(splitStr);StringBuilder chineseDateSbu = new StringBuilder();for (int i = 0; i < dates.length; i++) {chineseDateSbu.append(formatDigit(dates[i]));}return chineseDateSbu.toString();} catch (Exception e) {return null;}}/*** 截取后的年月日转换为中文* 例1:2000 --> 二〇〇〇* 例1:10 --> 十*/public static String formatDigit(String sign) {if (sign == null || "".equals(sign)) {return null;}char[] signChars = sign.toCharArray();StringBuilder chinese = new StringBuilder();if (signChars.length > 4 || signChars.length < 3) {for (char c : signChars) {chinese.append(MyChineseNumberEnum.getValue(String.valueOf(c)));}} else {if (sign.startsWith("0")){for (char c : signChars) {chinese.append(MyChineseNumberEnum.getValue(String.valueOf(c)));}}else {if (sign.equals(MyChineseNumberEnum.getValue(sign))){String subStr = sign.substring(0,sign.length()-1);String unitStr = sign.substring(sign.length()-1);chinese.append(MyChineseNumberEnum.getValue(subStr)+unitStr);}else {chinese.append(MyChineseNumberEnum.getValue(sign));}}}return chinese.toString();}public static String getCellType(ReadCellData cell, String cellVal){if (isStringTypeFromCell(cell,cellVal)){return "STRING";}switch (cell.getType()) {case NUMBER:String formatVal = cell.getDataFormatData().getFormat();// 将excel的类型处理成java可以识别的类型String newFormat = MyExcelTypeEnum.getFormatType(formatVal);if (newFormat == null || "".equals(newFormat)){newFormat = formatJavaType(formatVal);}if (DateUtils.isADateFormat(cell.getDataFormatData().getIndex(),formatVal)) {// 将数据格式化String originVal = getOtherDateFormat(cell,formatVal);if (!StringUtils.isEmpty(formatVal)&& (isChineseType(newFormat) || isChineseType(originVal) )) {return "STRING";}// 暂时简单处理,如果是包含h:mm:ss 默认按照时间格式处理,剩下默认按照时间戳处理if (StringUtils.contains(formatVal, "mm:ss")) {return "TIMESTAMP";} else {return "DATE";}} else if (StringUtils.equalsIgnoreCase("General",newFormat)) {return "STRING";} else {// 自定义的事件类型String otherDateType = getOtherDateType(cell.getDataFormatData().getIndex());if (!StringUtils.isEmpty(otherDateType)){return otherDateType;}if (!StringUtils.isEmpty(newFormat) && newFormat.matches(NUMBERTYPE_REGEX)) {if (StringUtils.equals("0", newFormat) || !newFormat.contains(".")) {return "NUMBER";} else {return "DOUBLE";}} else if (!StringUtils.isEmpty(newFormat) && newFormat.matches(EXCLUDE_SPECIFIC_REGEX)) {// 解决货币之类的数据类型判断如 ¥14return "STRING";} else {return "DOUBLE";}}case STRING:// 首行如果是表头汉字,全部都是stringString format = cell.getDataFormatData().getFormat();if (StringUtils.equalsIgnoreCase("General", format)) {return "STRING";} else if (!StringUtils.isEmpty(format) && format.matches(NUMBERTYPE_REGEX)) {if (StringUtils.equals("0", format)) {return "NUMBER";} else {return "DOUBLE";}} else if (isChineseType(MyExcelTypeEnum.getFormatType((format)))) {return "STRING";} else if (StringUtils.containsIgnoreCase(format, "mm:ss")) {return "TIMESTAMP";} else if (StringUtils.containsIgnoreCase(format, "yyyy")|| StringUtils.containsIgnoreCase(format, "mm")|| StringUtils.containsIgnoreCase(format, "dd")) {return "DATE";} else {return "STRING";}case BOOLEAN:return "BOOLEAN";default:return "STRING";}}/*** 用于解析excel表头及类型* @param ins* @param sheetName* @param startCell 起始单元格 默认A1* @param endCell 结束单元格* @return*/public static List<Map<String,String>> getSheetColumnNameAndType(InputStream ins, String sheetName, String startCell, String endCell){List<Map<String,String>> resList = new ArrayList<>();// 提起起始行数列数,结束行数列数Integer startColumnNum = excelCellToColumnNum(startCell);Integer startRowNum = excelCellToRowNum(startCell);Integer endColumnNum = excelCellToColumnNum(endCell);Integer endRowNum = excelCellToColumnNum(endCell);InputStream is = null;is = new DataInputStream(ins);MyExcelListener listen = new MyExcelListener();// 获取字段名称和类型listen.setNameAndTypeFlag(true);// 设置读取的列片区listen.setStartColumnNum(startColumnNum);listen.setEndColumnNum(endColumnNum);// 设置读取的行片区listen.setStartRowNum(startRowNum);listen.setEndRowNum(endRowNum);ExcelReaderBuilder read = EasyExcel.read(is, listen);read.sheet(sheetName).doRead();// 如果endColumnNum==null则无结束单元格,maxColumn就是excel最大的列,反之则取endColumnNumInteger maxColumn = endColumnNum != null ? endColumnNum : listen.getMaxColumnNum();// 组装首行内容 (受到其实单元格及结束单元格约束) headMap从0开始// 第一行数据(也就是表头数据),下标从0开始,startRowNum默认1(A1)Map<String, String> headMap = listen.getHeadValAndTypeMap();// 第二行数据,下标从0开始。包含内容及类型List<Map<String, String>> valList = listen.getContentValAndTypeList();Map<String, String> resHeadMap = null;String type = "STRING";for (int i = startColumnNum-1; i < maxColumn; i++) {resHeadMap = new HashMap<>();if (startRowNum==1){// 起始行数是1 head就会有数据,且是需要的数据if (valList.size()>0 && !StringUtils.isEmpty(valList.get(0).get(i+"_type"))){type = valList.get(0).get(i+"_type");} else if (!StringUtils.isEmpty(headMap.get(i+"_type"))) {type = headMap.get(i+"_type");}resHeadMap.put("columnName",headMap.getOrDefault(String.valueOf(i),""));resHeadMap.put("columnType",type);resList.add(resHeadMap);}else if(startRowNum>1 && valList.size()>0){// 起始行数是1 head中的数据并不是需要的数据,则从valList取 0 和 1 分别做 头和内容if (valList.size()>1 && valList.get(1)!=null && !StringUtils.isEmpty(valList.get(1).get(i+"_type"))){type = valList.get(1).get(i+"_type");} else if (!StringUtils.isEmpty(valList.get(0).get(i+"_type"))) {type = valList.get(0).get(i+"_type");}resHeadMap.put("columnName",valList.get(0).getOrDefault(String.valueOf(i),""));resHeadMap.put("columnType",type);resList.add(resHeadMap);}}return resList;}public static String checkExcelCellString(String startCell, String endCell) {// 起始单元格校验if (!StringUtils.isEmpty(startCell)) {if (!checkExcelCellSpecs(startCell)) {// 起始单元格不符合规范return "起始单元格不符合规范";}} else {// 起始单元格不得为空return "起始单元格不得为空";}// 结束单元格但如果不为空,则需要合法校验if (!StringUtils.isEmpty(endCell)) {if (!checkExcelCellSpecs(endCell)) {// 结束单元格不符合规范return "结束单元格不符合规范";}}// 单元格全部合法后,进行逻辑约束校验,起始单元格后的数字要小于等于结束单元格后缀// 列数校验 结束列数 >= 起始列数Integer startColumnNum = excelCellToColumnNum(startCell);Integer endColumnNum = excelCellToColumnNum(endCell);// 行数校验 结束行数 >= 起始行数Integer startRowNum = excelCellToRowNum(startCell);Integer endRowNum = excelCellToRowNum(endCell);if (endColumnNum != null) {if (startColumnNum > endColumnNum){return "起始单元格列必须小于等于结束起始单元格列";}else if (startRowNum > endRowNum){return "起始单元格行必须小于等于结束起始单元格行";}}return "";}/*** 检验单元格合法性* @param excelCell* @return*/private static boolean checkExcelCellSpecs(String excelCell) {return !StringUtils.isEmpty(excelCell) && excelCell.matches(CELL_REGEX);}/** 提取单元格字母对应列,如果colStr==null,则返回null* A1 -> 1、B1 -> 2、C1 -> 3*/public static Integer excelCellToColumnNum(String colStr) {Integer result = null;if (!StringUtils.isEmpty(colStr)) {result = 0;int length = colStr.length();int j = 0;int num = 0;for (int i = 0; i < length; i++) {char ch = colStr.charAt(length - i - 1);if (String.valueOf(ch).matches(LETTER_START_REGEX)) {num = ch - 'A' + 1;num *= Math.pow(26, j);j++;result += num;}}}return result;}/*** 提取单元格对应行返回值:Integer* 提取单元格字母对应行,如果colStr==null,则返回null* A1 -> 1、B2 -> 2、C3 -> 3*/public static Integer excelCellToRowNum(String colStr) {String numStr = excelCellToRowString(colStr);if (StringUtils.isEmpty(numStr)) {return null;}return Integer.parseInt(numStr);}/*** 提取单元格对应行返回值:string* @param colStr* @return*/public static String excelCellToRowString(String colStr) {String res = null;if (!StringUtils.isEmpty(colStr)) {Matcher matcher = Pattern.compile(ROW_REGEX).matcher(colStr);if (matcher.find()) {res = matcher.group();}}return res;}/*** 英文缩写的日期处理* @param numericCellValue* @param formatVal* @return*/private static String getEnglishDate(double numericCellValue, String formatVal){String dateStr = DateFormatUtils.format(doubleToDate(numericCellValue), formatVal, Locale.ENGLISH);if (StringUtils.equals(formatVal,"MMMMM")){// excel显示格式为:Freturn StringUtils.substring(dateStr,0,1);} else if (StringUtils.equals(formatVal,"MMMMM-yy")) {// excel显示格式为:F-23String letters = null;Matcher matcher = Pattern.compile(MATCH_LETTERS).matcher(dateStr);if (matcher.find()) {letters = matcher.group();}if (!StringUtils.isEmpty(letters) && !StringUtils.isEmpty(dateStr)){return StringUtils.replace(dateStr,letters,StringUtils.substring(dateStr,0,1));}return dateStr;}return dateStr;}private static boolean isStringTypeFromCell(ReadCellData cell, String cellValue){// 如果数据中含有中文,则直接返回string格式if (!StringUtils.isEmpty(cellValue) && cellValue.matches(CHINESETYPE_REGEX)){return true;}String newFormatStr = MyExcelTypeEnum.getFormatKey(cell.getDataFormatData().getFormat());if (StringUtils.containsIgnoreCase(newFormatStr,"EN") || StringUtils.containsIgnoreCase(cellValue,"AM")|| StringUtils.containsIgnoreCase(cellValue,"PM")){return true;}short shortNum = cell.getDataFormatData().getIndex();switch (shortNum){case 46:// 1184426:00:00return true;}return false;}private static boolean isChineseType(String param){if (!StringUtils.isEmpty(param)){if(param.matches(CHINESETYPE_REGEX)|| param.contains("E")|| param.contains("MMMM")){return true;}}return false;}private static String getOtherDateType(short shortNum){switch (shortNum) {case 14:case 30:return "DATE";case 31:case 57:return "TIMESTAMP";}return null;}private static String formatJavaType(String formatVal){String fs = formatVal;int length = formatVal.length();StringBuilder sb = new StringBuilder(length);int separatorIndex;for(separatorIndex = 0; separatorIndex < length; ++separatorIndex) {char c = fs.charAt(separatorIndex);if (separatorIndex < length - 1) {char nc = fs.charAt(separatorIndex + 1);if (c == '\\') {switch (nc) {case ' ':case ',':case '-':case '.':case '\\':continue;}} else if (c == ';' && nc == '@') {++separatorIndex;continue;}}sb.append(c);}fs = sb.toString();// excel设置单元格格式 使用数值fs = StringUtils.replace(fs,"0_ ","0");fs = StringUtils.replace(fs,"0_)","0");if (date_ptrn4.matcher(fs).matches()) {return fs;} else {fs = date_ptrn5.matcher(fs).replaceAll("");fs = date_ptrn1.matcher(fs).replaceAll("");fs = date_ptrn2.matcher(fs).replaceAll("");separatorIndex = fs.indexOf(59);if (0 < separatorIndex && separatorIndex < fs.length() - 1) {fs = fs.substring(0, separatorIndex);}return fs;}}private static Date doubleToDate(Double date){Calendar base = Calendar.getInstance();base.set(1899, 11, 30, 0, 0, 0);base.add(Calendar.DATE, date.intValue());base.add(Calendar.MILLISECOND,(int)((date % 1) * 24 * 60 * 60 * 1000));return base.getTime();}}
enum MyChineseNumberEnum {/*** 0*/ZERO("0", "〇"),ZERO_DOUBLE("00", "〇〇"),/*** 1*/ONE("1", "一"),/*** 2*/TWO("2", "二"),/*** 3*/THREE("3", "三"),/*** 4*/FOUR("4", "四"),/*** 5*/FIVE("5", "五"),/*** 6*/SIX("6", "六"),/*** 7*/SEVEN("7", "七"),/*** 8*/EIGHT("8", "八"),/*** 9*/NINE("9", "九"),/*** 10*/TEN("10", "十"),/****/ELEVEN("11", "十一"),/****/TWELVE("12", "十二"),/****/THIRTEEN("13", "十三"),/****/FOURTEEN("14", "十四"),/****/FIFTEEN("15", "十五"),/****/SIXTEEN("16", "十六"),/****/SEVENTEEN("17", "十七"),/****/EIGHTEEN("18", "十八"),/****/NINETEEN("19", "十九"),/****/TWENTY("20", "二十"),/****/TWENTY_ONE("21", "二十一"),/****/TWENTY_TWO("22", "二十二"),/****/TWENTY_THREE("23", "二十三"),/****/TWENTY_FOUR("24", "二十四"),/****/TWENTY_FIVE("25", "二十五"),/****/TWENTY_SIX("26", "二十六"),/****/TWENTY_SEVEN("27", "二十七"),/****/TWENTY_EIGHT("28", "二十八"),/****/TWENTY_NINE("29", "二十九"),/****/THIRTY("30", "三十"),/****/THIRTY_ONE("31", "三十一"),;private final String numberCode;private final String chineseCode;MyChineseNumberEnum(String numberCode, String chineseCode) {this.numberCode = numberCode;this.chineseCode = chineseCode;}public String getNumberCode() {return numberCode;}public String getChineseCode() {return chineseCode;}static final Map<String, String> SEND_METHOD_MAP = new HashMap<>();static {for (MyChineseNumberEnum code : MyChineseNumberEnum.values()) {SEND_METHOD_MAP.put(code.getNumberCode(), code.getChineseCode());}}public static String getValue(String key) {return SEND_METHOD_MAP.getOrDefault(key,key);}
}
enum MyExcelTypeEnum {// &$# 为二〇〇五年一月十五日格式数据的再次格式化标识DATE_CAP_1("[DBNum1][$-804]yyyy\"年\"m\"月\"d\"日\"","yyyy年"+SPECTYPESIGN+"M月"+SPECTYPESIGN+"dd日"),DATE_CAP_2("[DBNum1][$-804]yyyy\"年\"m\"月\"","yyyy年"+SPECTYPESIGN+"M月"),DATE_CAP_3("[DBNum1][$-804]m\"月\"d\"日\"","M月"+SPECTYPESIGN+"dd日"),DATE_CAP_4("[DBNum1]h\"时\"mm\"分\"","H时"+SPECTYPESIGN+"mm分"),DATE_CAP_5("[DBNum1]上午/下午h\"时\"mm\"分\"","aa"+SPECTYPESIGN+"h时"+SPECTYPESIGN+"mm分"),DATE_EN_SHORTHAND_1("d-mmm","d-MMM"),DATE_EN_SHORTHAND_2("d-mmm-yy","d-MMM-yy"),DATE_EN_SHORTHAND_3("dd\\-mmm\\-yy","d-MMM-yy"),DATE_EN_SHORTHAND_4("mmm-yy","MMM-yy"),DATE_EN_SHORTHAND_5("mmmm\\-yy","MMMM-yy"),DATE_EN_SHORTHAND_6("mmmm-yy","MMMM-yy"),DATE_EN_SHORTHAND_7("mmmmm","MMMMM"),DATE_EN_SHORTHAND_8("mmmmm\\-yy","MMMMM-yy"),DATE_TIME_1("h\"时\"mm\"分\"","H时mm分"),DATE_TIME_2("h\"时\"mm\"分\"ss\"秒\"","H时mm分ss秒"),DATE_TIME_3("上午/下午h\"时\"mm\"分\"","aah时mm分"),DATE_TIME_4("上午/下午h\"时\"mm\"分\"ss\"秒\"","aah时mm分ss秒"),DATE_EN_1("yyyy/m/d\\ h:mm\\ AM/PM","yyyy/M/d h:mm aa"),DATE_EN_2("h:mm\\ AM/PM","h:mm aa"),DATE_EN_3("h:mm:ss\\ AM/PM","h:mm:ss aa");String excelType;String formatType;MyExcelTypeEnum(String excelType, String formatType) {this.excelType = excelType;this.formatType = formatType;}public static String getFormatType(String excelType) {for (MyExcelTypeEnum excelTypeEnum : MyExcelTypeEnum.values()) {if (excelTypeEnum.excelType.equals(excelType)) {return excelTypeEnum.formatType;}}return null;}public static String getFormatKey(String excelType) {for (MyExcelTypeEnum excelTypeEnum : MyExcelTypeEnum.values()) {if (excelTypeEnum.excelType.equals(excelType)) {return excelTypeEnum.name();}}return excelType;}
}class StringUtils{public static boolean isEmpty(String str){return str==null || "".equals(str);};public static String replace(String str, String oldStr){if (isEmpty(str)) return str;return str.replace(oldStr,"");};public static String replace(String str, String oldStr, String newStr){if (isEmpty(str)) return str;return str.replace(oldStr,newStr);};public static boolean containsIgnoreCase(String str, String containsStr){if (isEmpty(str)) return false;return str.toLowerCase().contains(containsStr.toLowerCase());};public static boolean equals(String str, String anObject){if (isEmpty(str)) return false;return str.equals(anObject);};public static boolean equalsIgnoreCase(String str, String anObject){if (isEmpty(str)) return false;return str.equalsIgnoreCase(anObject);};public static boolean contains(String str, String anObject){if (isEmpty(str)) return false;return str.contains(anObject);};public static String substring(String str, int beginIndex, int endIndex){if (isEmpty(str)) return str;return str.substring(beginIndex, endIndex);};
}
测试类
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;public class Mytest {public static void main(String[] args) throws Exception {String filep = "C:\\test.xlsx";String sheet = "Sheet1";String startCell = "A1";String end = null;//"C3";File file = new File(filep);InputStream ins2 = new FileInputStream(file);String checkMsg = MyExcelUtils.checkExcelCellString(startCell,end);System.out.println(checkMsg);List<Map<String,String>> ll =MyExcelUtils.getSheetColumnNameAndType(ins2,sheet,startCell,end);ll.forEach(e-> System.out.println("columnName---"+e.get("columnName")+"---"+e.get("columnType")));}
}
部分测试结果
备注
excel类型比较多,这里适配的也只是一部分,逐步完善
其他
Chat2DB:https://easyexcel.opensource.alibaba.com/docs/current/
官方网站:https://easyexcel.opensource.alibaba.com/
github地址:https://github.com/alibaba/easyexcel
gitee地址:https://gitee.com/easyexcel/easyexcel
相关文章:

【解析excel】利用easyexcel解析excel
【解析excel】利用easyexcel解析excel POM监听类工具类测试类部分测试结果备注其他 EasyExcel Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&…...
JQuery操作单选按钮Radio和复选框checkbox
获取选中值: $(input:radio:checked).val();$("input[typeradio]:checked").val();$("input[namerd]:checked").val();$("input[idrand_question]:checked").val();设置第一个Radio为选中值: $(input:radio:…...

7.28 作业 QT
手动完成服务器的实现,并具体程序要注释清楚: widget.h: #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器类 #include <QTcpSocket> //客户端类 #include <QMessageBox> //对话框类 #include …...
HTML <pre> 标签
定义和用法 pre 元素可定义预格式化的文本。被包围在 pre 元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。 <pre> 标签的一个常见应用就是用来表示计算机的源代码。 可以导致段落断开的标签(例如标题、"><p> 和 标签"><a…...

查询结果元数据-MetaData对象、数据库工具类的封装、通过反射实现数据查询的封装
六、查询结果元数据-MetaData对象 七、数据库工具类的封装 1、PropertieUtil类 2、DbUtil类 3、DBHepler类 查询: 4、TestDb测试类: 更新: 1)插入: 2)修改: 3)删除: 查…...

【Minio中间件】上传图片并Vue回显
流程: 目录 1.文件服务器Minio的安装 1.1 下载Minio安装后,新建1个data文件夹。并在该安装目录cmd 敲命令。注意不要进错目录。依次输入 1.2 登录Minio网页端 1.3 先建1个桶(buckets),点击create a bucket 2. Spr…...

Jmeter配置不同业务请求比例,应对综合场景压测
需求: 每次向服务器发出请求时,先生成一个随机数,我们对随机数的取值划分若干个范围(对应若干个业务请求),然后对随机数进行判断,当随机数落在某个范围内,就可以执行对应的请求。比…...

数学分析:流形的线性代数回顾
因为是线性的,所以可以把所有的系数都提取出去。这也是多重线性代数的性质。可以看成基本的各项自变量的乘法。 这里可以看到两个不同基向量下,他们的坐标转化关系。 引出了张量积,也就是前面提到的内容。 对偶空间的例子总是比较美好。 因为…...
前端请求后端接口返回错误码
1、如果 HTTP Code 是 2xx 范围内的,那通常表明请求已经成功处理,并且可以根据具体的 HTTP Code 进一步判断请求的处理结果。比如: HTTP Code 200 表明请求成功,并返回了请求资源;HTTP Code 204 表明请求成功…...
【Java Web】Nacos 介绍和安装教程
文章目录 1. Nacos 介绍1.1 Nacos 的定义1.2 Nacos 的主要功能1.2.1 服务注册与发现1.2.2 配置管理1.2.3 动态 DNS 服务1.2.4 服务和元数据管理 1.3 Nacos 的适用场景1.3.1 微服务架构1.3.2 动态配置管理1.3.3 多环境部署1.3.4 云原生应用 2. Nacos 的核心组件2.1 服务注册与发…...

web漏洞-java安全(41)
这个重点是讲关于java的代码审计,看这些漏洞是怎么在java代码里面产生的。 #Javaweb 代码分析-目录遍历安全问题 这个漏洞原因前面文章有,这次我们看看这个漏洞如何在代码中产生的,打开靶场 解题思路就是通过文件上传,上传文件…...

用CSS和HTML写一个水果库存静态页面
HTML代码: <!DOCTYPE html> <html> <head><link rel"stylesheet" type"text/css" href"styles.css"> </head> <body><header><h1>水果库存</h1></header><table>…...

【回眸】备考PMP考点汇总 三(距离考试还有20天)
目录 前言 【回眸】备考PMP考点汇总 三(距离考试还有20天) 29、管理质量 30、获取资源 31、建设团队 32、管理团队 33、管理沟通 34、实施风险应对 35、实施采购 36、管理相关方参与 37、监控项目工作(10%) 38、实施整…...
新房的收房验房注意事项
文章目录 流程注意事项准备检查材料手续整体结构验收水电检测门窗结构地面工程墙面工程顶面工程阳台厨房卫生间户外设施 流程注意事项 只要发现问题,不管大小,都要在相关文件或表格中记录下来,而不管开发商以及陪同的收房人员如何花言巧语。…...

ARM裸机-5
1、可编程器件的编程原理 1.1、电子器件的发展方向 模拟器件-->数字器件 ASIC-->可编程器件 1.2、可编程器件的特点 CPU在固定频率的时钟控制下节奏运行。 CPU可以通过总线读取外部存储设备中的二进制指令集,然后解码执行。 这些可以被CPU解码执行的二进制指…...
SpringCloud学习路线(11)——分布式搜索ElasticSeach场景使用
一、DSL查询文档 (一)DSL查询分类 ES提供了基于JSON的DSL来定义查询。 1、常见查询类型: 查询所有: 查询出所有的数据,例如,match_all全文检索(full text)查询: 利用…...
负数补码表示
负数补码作用 在计算机中加法和减法采用同一电路,即用加法表示减法,如7 - 2 5变成7 ( -2) 5,这样减法的电路不用另行设计,但计算机中数据以二进制存储,没有负号,因此设计负数补码代…...

ChatGPT结合知识图谱构建医疗问答应用 (一) - 构建知识图谱
一、ChatGPT结合知识图谱 在本专栏的前面文章中构建 ChatGPT 本地知识库问答应用,都是基于词向量检索 Embedding 嵌入的方式实现的,在传统的问答领域中,一般知识源采用知识图谱来进行构建,但基于知识图谱的问答对于自然语言的处理…...

C++ 类和对象
面向过程/面向对象 C语言是面向过程,关注过程,分析出求解问题的步骤,通过函数调用逐步解决问题 C是基于面对对象的,关注的是对象——将一件事拆分成不同的对象,依靠对象之间的交互完成 引入 C语言中结构体只能定义…...

c# 此程序集中已使用了资源标识符
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 CS1508 此程序集中已使用了资源标识符“BMap.NET.WindowsForm.BMapControl.resources” BMap.NET.WindowsForm D:\MySource\Decompile\BMap.NET.WindowsForm\CSC 1 活动 运行程序时&a…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...