当前位置: 首页 > news >正文

【解析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。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&…...

JQuery操作单选按钮Radio和复选框checkbox

获取选中值&#xff1a; $(input:radio:checked).val()&#xff1b;$("input[typeradio]:checked").val();$("input[namerd]:checked").val();$("input[idrand_question]:checked").val();设置第一个Radio为选中值&#xff1a; $(input:radio:…...

7.28 作业 QT

手动完成服务器的实现&#xff0c;并具体程序要注释清楚: 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类 查询&#xff1a; 4、TestDb测试类&#xff1a; 更新&#xff1a; 1&#xff09;插入&#xff1a; 2&#xff09;修改&#xff1a; 3&#xff09;删除&#xff1a; 查…...

【Minio中间件】上传图片并Vue回显

流程&#xff1a; 目录 1.文件服务器Minio的安装 1.1 下载Minio安装后&#xff0c;新建1个data文件夹。并在该安装目录cmd 敲命令。注意不要进错目录。依次输入 1.2 登录Minio网页端 1.3 先建1个桶&#xff08;buckets&#xff09;&#xff0c;点击create a bucket 2. Spr…...

Jmeter配置不同业务请求比例,应对综合场景压测

需求&#xff1a; 每次向服务器发出请求时&#xff0c;先生成一个随机数&#xff0c;我们对随机数的取值划分若干个范围&#xff08;对应若干个业务请求&#xff09;&#xff0c;然后对随机数进行判断&#xff0c;当随机数落在某个范围内&#xff0c;就可以执行对应的请求。比…...

数学分析:流形的线性代数回顾

因为是线性的&#xff0c;所以可以把所有的系数都提取出去。这也是多重线性代数的性质。可以看成基本的各项自变量的乘法。 这里可以看到两个不同基向量下&#xff0c;他们的坐标转化关系。 引出了张量积&#xff0c;也就是前面提到的内容。 对偶空间的例子总是比较美好。 因为…...

前端请求后端接口返回错误码

1、如果 HTTP Code 是 2xx 范围内的&#xff0c;那通常表明请求已经成功处理&#xff0c;并且可以根据具体的 HTTP Code 进一步判断请求的处理结果。比如&#xff1a; HTTP Code 200 表明请求成功&#xff0c;并返回了请求资源&#xff1b;HTTP Code 204 表明请求成功&#xf…...

【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的代码审计&#xff0c;看这些漏洞是怎么在java代码里面产生的。 #Javaweb 代码分析-目录遍历安全问题 这个漏洞原因前面文章有&#xff0c;这次我们看看这个漏洞如何在代码中产生的&#xff0c;打开靶场 解题思路就是通过文件上传&#xff0c;上传文件…...

用CSS和HTML写一个水果库存静态页面

HTML代码&#xff1a; <!DOCTYPE html> <html> <head><link rel"stylesheet" type"text/css" href"styles.css"> </head> <body><header><h1>水果库存</h1></header><table>…...

【回眸】备考PMP考点汇总 三(距离考试还有20天)

目录 前言 【回眸】备考PMP考点汇总 三&#xff08;距离考试还有20天&#xff09; 29、管理质量 30、获取资源 31、建设团队 32、管理团队 33、管理沟通 34、实施风险应对 35、实施采购 36、管理相关方参与 37、监控项目工作&#xff08;10%&#xff09; 38、实施整…...

新房的收房验房注意事项

文章目录 流程注意事项准备检查材料手续整体结构验收水电检测门窗结构地面工程墙面工程顶面工程阳台厨房卫生间户外设施 流程注意事项 只要发现问题&#xff0c;不管大小&#xff0c;都要在相关文件或表格中记录下来&#xff0c;而不管开发商以及陪同的收房人员如何花言巧语。…...

ARM裸机-5

1、可编程器件的编程原理 1.1、电子器件的发展方向 模拟器件-->数字器件 ASIC-->可编程器件 1.2、可编程器件的特点 CPU在固定频率的时钟控制下节奏运行。 CPU可以通过总线读取外部存储设备中的二进制指令集&#xff0c;然后解码执行。 这些可以被CPU解码执行的二进制指…...

SpringCloud学习路线(11)——分布式搜索ElasticSeach场景使用

一、DSL查询文档 &#xff08;一&#xff09;DSL查询分类 ES提供了基于JSON的DSL来定义查询。 1、常见查询类型&#xff1a; 查询所有&#xff1a; 查询出所有的数据&#xff0c;例如&#xff0c;match_all全文检索&#xff08;full text&#xff09;查询&#xff1a; 利用…...

负数补码表示

负数补码作用 在计算机中加法和减法采用同一电路&#xff0c;即用加法表示减法&#xff0c;如7 - 2 5变成7 &#xff08; -2&#xff09; 5&#xff0c;这样减法的电路不用另行设计&#xff0c;但计算机中数据以二进制存储&#xff0c;没有负号&#xff0c;因此设计负数补码代…...

ChatGPT结合知识图谱构建医疗问答应用 (一) - 构建知识图谱

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

C++ 类和对象

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

c# 此程序集中已使用了资源标识符

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

LangChainGo入门指南:Go语言实现与OpenAI/Qwen模型集成实战

目录 1、什么是langchainGo2、langchainGo的官方地址3、LangChainGo with OpenAI3-1、前置准备3-2、安装依赖库3-3、新建模型客户端3-4、使用模型进行对话 4、总结 1、什么是langchainGo langchaingo是langchain的go语言实现版本 2、langchainGo的官方地址 官网&#xff1a;…...

短视频矩阵系统源码新发布技术方案有那几种?

短视频矩阵运营在平台政策频繁更迭的浪潮中&#xff0c;已成为内容分发的核心战场。行业领先者如筷子科技、云罗抖去推、超级编导等平台&#xff0c;其稳定高效的代发能力背后&#xff0c;离不开前沿技术方案的强力支撑。本文将深入剖析当前主流的六大短视频矩阵系统代发解决方…...

微算法科技(NASDAQ:MLGO)基于信任的集成共识和灰狼优化(GWO)算法,搭建高信任水平的区块链网络

随着数字化转型的加速&#xff0c;区块链技术作为去中心化、透明且不可篡改的数据存储与交换平台&#xff0c;正逐步渗透到金融、供应链管理、物联网等多个领域&#xff0c;探索基于信任的集成共识机制&#xff0c;并结合先进的优化算法来提升区块链网络的信任水平&#xff0c;…...

几何引擎对比:OpenCasCade、ACIS、Parasolid和CGM

概述 从技术架构与行业实践来看&#xff0c;OpenCasCade 凭借开源生态与轻量化设计形成差异化竞争力&#xff0c;尤其适合预算敏感、需定制开发或依赖开源工具链的场景&#xff1b;而 ACIS、Parasolid 等商业内核则通过工业级精度优化与主流 CAD 深度绑定占据大型企业市场&…...

如何使用k8s安装redis呢

在Kubernetes (k8s) 上安装Redis 在Kubernetes上安装Redis有几种方法&#xff0c;下面我将介绍两种常见的方式&#xff1a;使用StatefulSet直接部署和使用Helm chart部署。 一、安装redis 1.1 拉去ARM镜像&#xff08;7.4.2&#xff09; docker pull registry.cn-hangzhou.ali…...

高考:如何合理选择学科、专业以及职业

如何合理选择学科、专业以及职业 一、自我认知&#xff1a;明确自身兴趣与优势&#xff08;一&#xff09;兴趣探索&#xff08;二&#xff09;能力评估&#xff08;三&#xff09;价值观与目标 二、外部调研&#xff1a;深入了解学科、专业与职业&#xff08;一&#xff09;学…...

ruoyi-plus-could 负载均衡 通过 Gateway模块配置负载均衡

这个很简单的&#xff0c;其实都不用配置。 在nacos中ruoyi-gateway.yml配置文件里面&#xff1a; 其实他已经给我们配置好了&#xff0c;只要uri&#xff1a;lb有【lb】就表示负载均衡配置 我们只需要在启动服务的时候改下端口就可以。 然后通过小工具测试下&#xff1a; 结…...

【PhysUnits】16.1 完善Var 结构体及其运算(variable.rs)

一、源码 这段代码定义了一个泛型结构体 Var&#xff0c;并为它实现了各种数学运算。 /** 变量结构体 Var* 该结构体泛型参数 T 需满足 Numeric 约束*/use core::ops::{Neg, Add, Sub, Mul}; use crate::constant::Integer; /// 定义 Numeric trait&#xff0c;约束 T 必须实…...

JUnit​​ 和 ​​Mockito​​ 的详细说明及示例,涵盖核心概念、常用注解、测试场景和实战案例。

一、JUnit 详解 1. JUnit 核心概念 ​​测试类​​&#xff1a;以 Test 结尾的类&#xff08;或通过 Test 注解标记的方法&#xff09;。​​断言&#xff08;Assertions&#xff09;​​&#xff1a;验证预期结果与实际结果是否一致&#xff08;如 assertEquals()&#xff0…...

GNSS终端授时方式-合集:PPS、B码、NTP、PTP、单站授时,共视授时

GNSS接收机具备授时功能&#xff0c;能够对外输出高精度的时间信息&#xff0c;并通过多种接口、多种形式进行时间信息的传递。 step by step介绍GNSS卫星导航定位基本原理&#xff0c;为什么定位需要至少4个卫星&#xff1f;这个文章的最后&#xff0c;我们介绍了为什么GNSS接…...