【解析excel】利用easyexcel解析excel

news2024/11/27 1:24:29

在这里插入图片描述

【解析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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【代码随想录 | Leetcode | 第十一天】字符串 | 反转字符串 | 反转字符串 II | 替换空格 | 反转字符串中的单词 | 左旋转字符串

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来字符串~反转字符串 | 反转字符串 II | 替换空格 | 反转字符串中的单词 | 左旋转字符串的分享✨ 目录 前言344. 反转字符串541. 反转字符串 II剑指 Offer 05. 替换空格151. 反转字符串中的单词剑…

Linux:centos7:zabbix4.0(安装,监控》Linux》Windows》网络设备)

环境 centos7&#xff08;zabbix服务器&#xff09;内网ip&#xff1a;192.168.254.11 外网ip&#xff1a;192.168.0.188&#xff08;去网络yum源下载&#xff09; centos7&#xff08;被监控端&#xff09;内网ip&#xff1a;192.168.254.33win10&#xff08;被监控端&…

怎么学习Java安全性和加密相关知识?

学习Java安全性和加密相关知识是非常重要的&#xff0c;特别是在开发涉及敏感数据的应用程序时。以下是学习Java安全性和加密的一些建议&#xff1a; 基础知识&#xff1a; 首先&#xff0c;了解计算机网络安全的基本概念&#xff0c;包括加密、解密、哈希算法、数字签名等。…

图书管理系统--进阶(动态开辟内存 + 保存数据到文件)

文章目录 动态开辟内存优化改进图书管理系统类型改进初始化图书管理系统的函数改进添加图书信息的函数增加销毁图书信息管理系统的函数 保存数据到文件优化保存图书信息管理系统数据到文件读取数据到图书信息管理系统 完整的代码展示 在 C语言实现图书管理系统的所有基本功能详…

当下哪些行业最容易被AI替代?

随着人工智能&#xff08;AI&#xff09;技术的不断发展&#xff0c;越来越多的行业受到AI的冲击。从工业制造到为人服务&#xff0c;在我们许多未察觉的领域&#xff0c;AI正在逐渐取代传统的人类劳动力。那么哪些行业最容易被AI替代呢&#xff1f; 一、制造业和生产线 制造业…

overflow-x:scroll不生效

父元素的宽度一定要设置,不要使用100%, 使用100%可能会导致父跟子元素的宽度一样, 从而无法滑动 父元素: overflow-x:scroll 根据gpt的答案:

Android 6.0 版本中插件化沙盒环境下IllegalArgumentException Unknown package异常

近期从bugly上发现某个渠道包报错 Unknown package异常问题&#xff0c;经过framework层源码分析&#xff0c;发现是插件化api兼容性问题,该渠道包是运行在沙盒环境(233乐园)。 现象 java 报错栈&#xff1a; 其他设备&#xff0c;次数信息&#xff1a;目前发生android 6.0系…

QT【day2】

完善登录框&#xff1a; //main头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include<QDebug> //信息调试类&#xff0c;用于打印输出 #include<QIcon> //图标头文件 #include<QPushButton> //按钮类头文件 #include…

Web_python_template_injection

考察模版Python flask的模版注入&#xff0c;页面没有回显&#xff0c;只有一段字符串&#xff0c;尝试直接利用Python传参 完全没回显 但是如果直接访问的话&#xff0c;就会利用url报错&#xff0c;将结果给执行带出来&#xff0c;那说明我就可以直接利用这个性质进行ssti模版…

SQL注入--题目

联合查询注入&#xff1a; bugku-这是一个神奇的登录框 手工注入&#xff1a; 点吧&#xff0c;输入0’发现还是&#xff1a; 输入0" 发现报错&#xff1a; 确定可以注入&#xff0c;判断字段有多少个 0"order by 1,2,3# 发现&#xff1a; 说明有两列。 输入 0&qu…

赞同高频接广?品牌复投成B站粉丝催更利器

一直以来&#xff0c;电商行业是B站公开的前五大广告主之一。今年618电商节期间B站发布数据称&#xff0c;B站电商类广告收入迎来爆发&#xff0c;同比增长超400%。其中&#xff0c;站内带货视频数量同比增长近8倍&#xff0c;带货直播场次同比增长近7.5倍。 B站作为中长视频为…

只需要5个技巧,就能让你的独立站更吸引客户

人类很简单——我们都更喜欢第一眼看起来令人心情愉悦的的东西。独立站卖家自由度更高&#xff0c;可以添加很多令人惊叹的产品、调整更优惠的价格并设置多种语言选择来吸引访问者购买&#xff0c;但网站的设计仍然是大多数访问者判断独立站品牌的第一要素。根据可靠调研得知&a…

Linux基础IO(二)

Linux基础IO(二) 文章目录 Linux基础IO(二)缓冲区为什么会有缓冲区缓冲区刷新策略系统有缓冲区吗&#xff1f; 文件系统什么是inode 软硬链接软链接硬链接热知识总结 文件的三个时间动静态库什么是动静态库怎么理解动静态库原理使用 制作一个库前置准备自制静态库使用自制的静态…

根据索引值计算item所在行列索引序号

getRowColIndex(itemIndex 0, colCount 3) {//必选参数&#xff1a;itemIndex是当前元素的索引值&#xff0c;从0开始计算&#xff08;如果是从1开始&#xff0c;则需将传入值-1&#xff0c;即itemIndex--&#xff09;//必选参数&#xff1a;colCount是每一行显示多少个元素r…

使用Anaconda创建虚拟环境并添加到Jupyter notebook内核

1.修改虚拟环境的存放位置&#xff08;可选&#xff09; Windows&#xff1a;打开文件C:\Users\DongZhaoCheng(对应到你本人的用户名)\.condarc,添加envs_dirs: [D:\003SoftDevTool\anaconda_env]&#xff08;对应到你自己执行的目录&#xff09;,保存退出 2.进入系统终端创建…

Windows 11隐藏功能大揭秘

Windows 11隐藏功能大揭秘&#xff01;你了解多少&#xff1f; 不知道你是否已经升级到了Windows 11系统&#xff1f;毕竟&#xff0c;这个备受期待的新系统已经发布有一段时间了&#xff0c;相信大多数用户都已经开始尝试使用微软的新系统了吧。但是&#xff0c;你真的熟悉它…

查看进程环境变量

1、Linux 每一个进程的环境变量存储在 /proc/$PID/environ 中。 而 PID 可用通过 ps / jps(java 进程) 等命令查看。 ps -ef | grep xxxjps文件 /proc/$PID/environ 变量以namevalue的形式描述。多个变量之间由 null&#xff08;‘\0’&#xff09;分隔&#xff0c;为了便于阅…

嵌入式和 Java 走哪条路?

JAVA和嵌入式各有千秋&#xff0c;看个人取舍。 想挣钱挣得快一点&#xff0c;挣得多一点&#xff0c;那就选Java&#xff0c;但有中年危机。 想细水长流一点的&#xff0c;选嵌入式&#xff0c;挣钱挣得慢一点&#xff0c;也稳一点&#xff0c;挣得久一点&#xff0c;中年危…

Beamer学习手册

Beamer学习手册 ZJU Beamer模板解读参考链接 谨以本文记录一下学习beamer的过程 本文基于 Overleaf 的 ZJU Beamer模板 进行学习并修改模板&#xff0c;感谢前辈的贡献&#xff01; ZJU Beamer模板解读 首先在 Overleaf 下载 ZJU Beamer模板 &#xff0c;解压压缩包后&#xf…

Docker网络与Docker Compose服务编排

docker网络 docker是以镜像一层一层构建的&#xff0c;而基础镜像是linux内核&#xff0c;因此docker之间也需要通讯&#xff0c;那么就需要有自己的网络。就像windows都有自己的内网地址一样&#xff0c;每个docker容器也是有自己的私有地址的。 docker inspect [docker_ID]…