【解析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…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...
