【解析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,不可为null
    private int maxColumnNum;
    // sheet中行数,默认为1,因为从第二行开始读取
    private int maxRowNum = 1;

    @Override
    public 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);
        }

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    @Override
    public 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:00
            return 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 AM
            return DateFormatUtils.format(doubleToDate(cell.getNumberValue().doubleValue()), newFormatStr, Locale.ENGLISH);
        } else if (StringUtils.contains(newFormatStr,"MMM")) {
            // J 、J-23
            return 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)) {
                        // 解决货币之类的数据类型判断如 ¥14
                        return "STRING";
                    } else {
                        return "DOUBLE";
                    }
                }
            case STRING:
                // 首行如果是表头汉字,全部都是string
                String 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最大的列,反之则取endColumnNum
        Integer 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显示格式为:F
            return StringUtils.substring(dateStr,0,1);
        } else if (StringUtils.equals(formatVal,"MMMMM-yy")) {
            // excel显示格式为:F-23
            String 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:00
                return 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/51897.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iOS开发-自定义TabbarController与Tabbar按钮Badge角标

iOS开发-自定义Tabbar按钮Badge角标 Tabbar是放在APP底部的控件。UITabbarController是一个非常常见的一种展示模式了。比如微信、QQ都是使用tabbar来进行功能分类的管理。 一、实现自定义Tabbar 我这里Tabbar继承于系统的UITabBar&#xff0c;定义背景图、线条的颜色、tab…

AI编程常用工具 Jupyter Notebook

点击上方蓝色字体&#xff0c;选择“设为星标” 回复”云原生“获取基础架构实践 深度学习编程常用工具 我们先来看 4 个常用的编程工具&#xff1a;Sublime Text、Vim、Jupyter。虽然我介绍的是 Jupyter&#xff0c;但并不是要求你必须使用它&#xff0c;你也可以根据自己的喜…

VS Code环境配置问题

VS Code 环境配置问题 文章目录 VS Code 环境配置问题配置 C问题解决不乱码只显示结果避免闪退&#xff0c;中文乱码 配置 Java下载 JDKJDK 环境配置安装插件 配置 C 跟着官网教程&#xff08;英文版&#xff09;和其他博客配置了一遍&#xff0c;却遇到了很多小问题&#xff…

应用无线鼠标中的2.4GHz无线收发芯片

无线键盘和无线鼠标作为现代办公环境中常见的工具&#xff0c;为我们的工作带来了便利。无线键盘和无线鼠标的工作原理都是基于无线技术实现的&#xff0c;其中常见的是2.4GHz无线技术。让我们一起来详细了解一下它们的工作原理。 无线鼠标的原理非常简单,鼠标部分工作与传统鼠…

Spring中IOC容器常用的接口和具体的实现类

在Spring框架没有出现之前&#xff0c;在Java语言中&#xff0c;程序员们创建对象一般都是通过关键字new来完成&#xff0c;那时流行一句话“万物即可new&#xff0c;包括女朋友”。但是这种创建对象的方式维护成本很高&#xff0c;而且对于类之间的相互关联关系很不友好。鉴于…

【Tomcat---1】IDEA控制台tomcat日志输出乱码解决

一、修改IDEA的文件编码配置为UTF-8 二、修改IDEA的vmoptions文件&#xff0c;添加-Dfile.encodingUTF-8 到Tomcat目录/conf文件夹修改logging.properties 重启idea即可。采用统一的编码

解决在云服务器开放端口号以后telnet还是无法连接的问题

这里用阿里云服务器举例&#xff0c;在安全组开放了对应的TCP端口以后。使用windows的cmd下的telnet命令&#xff0c;还是无法正常连接。 telnet IP地址 端口号解决方法1&#xff1a; 在轻量服务器控制台的防火墙规则中添加放行端口。 阿里云-管理防火墙 如图&#xff0c;开放…

CAN转ETHERCAT网关将CAN 总线和 ETHERCAT 网络连接方法

由于好多现场会出现将CAN总线的设备接到EtherCAT网络中&#xff0c;由于协议的不相同&#xff0c;不能直接进行连接&#xff0c;现需一种能同时兼容CAN 总线和ETHERCAT网络的一种设备&#xff0c;由此捷米JM-ECT-CAN 是自主研发的一款 ETHERCAT 从站功能的通讯网关。该产品主要…

【目标检测论文解读复现NO.33】改进YOLOv5的新能源电池集流盘缺陷检测方法

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

vuejs源码阅读之优化器

前面讲过vuejs中解析器是把html模版解析成AST&#xff0c;而优化器的作用是在AST中找到静态子树并打上标记。 静态子树是指的那些在AST中永远不会发生变化的节点。 例如&#xff0c;一个纯文本节点就是静态子树&#xff0c;而带变量的文本节点就不是静态子树&#xff0c;因为…

TCP网络通信编程之网络上传文件

【图片】 【思路解析】 【客户端代码】 import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException;/*** ProjectName: Study* FileName: TCPFileUploadClient* author:HWJ* Data: 2023/7/29 18:44*/ public class TCPFil…

如何快速模拟一个后端 API

第一步&#xff1a;创建一个文件夹&#xff0c;用来存储你的数据 数据&#xff1a; {"todos": [{ "id": 1, "text": "学习html44", "done": false },{ "id": 2, "text": "学习css", "…

【QT】Day 2

1> 继续完善登录框&#xff0c;当登录成功时&#xff0c;关闭登录界面&#xff0c;跳转到新的界面中 second.h #ifndef SECOND_H #define SECOND_H#include <QWidget>namespace Ui { class second; }class second : public QWidget {Q_OBJECTpublic:explicit second…

Abaqus 导出单元刚度矩阵和全局刚度矩阵

Abaqus 导出单元刚度矩阵和全局刚度矩阵 首次创建&#xff1a;2023.7.29 最后更新&#xff1a;2023.7.29 如有什么改进的地方&#xff0c;欢迎大家讨论&#xff01; 详细情况请查阅&#xff1a;Abaqus Analysis User’s Guide 一、Abaqus 导出单元刚度矩阵 1.生成单元刚度矩阵…

C语言手撕顺序表

目录 一、概念 1、静态顺序表&#xff1a;使用定长数组存储元素。 2、动态顺序表&#xff1a;使用动态开辟的数组存储 二、接口实现 1、对顺序表的初始化 2、对数据的销毁 3、对数据的打印 4、检查是否需要扩容 5、尾插 6、头插 7、尾删 8、头删 9、在pos位置插入x …

如何有效地使用ChatGPT写小说讲故事?

​构思故事情节&#xff0c;虽有趣但耗时&#xff0c;容易陷入写作瓶颈。ChatGPT可提供灵感&#xff0c;帮你解决写作难题。要写出引人入胜的故事&#xff0c;关键在于抓住八个要素——主题、人物、视角、背景、情节、语气、冲突和解决办法。 直接给出故事模板&#xff0c;你可…

WIZnet W5500-EVB-Pico 静态IP配置教程(二)

W5500是一款高性价比的 以太网芯片&#xff0c;其全球独一无二的全硬件TCP、IP协议栈专利技术&#xff0c;解决了嵌入式以太网的接入问题&#xff0c;简单易用&#xff0c;安全稳定&#xff0c;是物联网设备的首选解决方案。WIZnet提供完善的配套资料以及实时周到的技术支持服务…

TCP网络通信编程之字节流

目录 【TCP字节流编程】 // 网络编程中&#xff0c;一定是server端先运行 【案例1】 【思路分析】 【客户端代码】 【服务端代码】 【结果展示】 【案例2】 【题目描述】 【注意事项】 【服务端代码】 【客户端代码】 【代码结果】 【TCP字节流编程】 // 网络编程中&a…

《向量数据库指南》——Milvus Cloud2.2.12 易用性,可视化,自动化大幅提升

Milvus Cloud又迎版本升级,三大新特性全力加持,易用性再上新台阶! 近期,Milvus Cloud上线了 2.2.12 版本,此次更新不仅一次性增加了支持 Restful API、召回原始向量、json_contains 函数这三大特性,还优化了 standalone 模式下的 CPU 使用、查询链路等性能,用一句话总…

Mysql定时删除表数据

由于用户环境有张日志表每天程序都在狂插数据&#xff0c;导致不到一个月时间&#xff0c;这张日志表就高达200多万条记录&#xff0c;但是日志刷新较快&#xff0c;里面很多日志没什么作用&#xff0c;就写了个定时器&#xff0c;定期删除这张表的数据。 首先查看mysql是否开启…