Как читать Excel (xlsx) Тип данных столбца заголовка с использованием apache POI 4.X

#java #apache-poi

Вопрос:

Я изо всех сил пытался выяснить, как прочитать тип данных для каждого из столбцов заголовка. Я использую Apache POI 4.1.2 для чтения формата файла XLSX с использованием реализации XSSFWorkbook.

Бизнес-вариант использования: У меня есть excel с 22 ячейками( включает строковое, числовое, поле даты), если пользователь попытается изменить поле даты на строковое или Числовое на строковое или наоборот. Мне нужно выбросить ошибку, даже не читая строки данных. Таким образом, я могу обрабатывать строки данных в случае, если пользователь изменяет тип ( значение даты в varchar, числовое в строку).

Фрагмент кода Sudo:

 XSSFRow headerRow = sheet.getRow(rownum:0);
for(int cellNum=0; cellNum<22;cellNum  ){
  Cell cell = headerRow.getCell(cellNum);
  System.out.println(cell.getCellType()); 
// This always returns the string even though the header row has date and numeric fields)
}
 

Ни один поиск или рекомендация не помогли мне с какими-либо зацепками или решением, поэтому я ищу рабочее решение.

Ответ №1:

Невозможно определить типы столбцов на основе заголовков. Заголовки обычно представляют собой текст (строку), который мы вводим в первую строку файла. Они не содержат какого-либо определенного типа данных.

С другой стороны, если вам нужно понимать типы столбцов, вам нужно читать со 2-й строки файла (оставляя заголовки).

Вот некоторая реализация, которая довольно хорошо справляется с анализом типов столбцов. Вы можете указать, сколько строк следует рассмотреть для анализа и какой лист следует проанализировать

Код:

 import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class Test {

    public static void main(String[] args) {
        
        String excelFileName = "C:\Users\test\Downloads\test.xlsx";
        int sheet_num = 0;
        int maxRows_ToAnalyze = 5;
        
        Map<Integer, String> result = analyzeColumnTypes(excelFileName, sheet_num, maxRows_ToAnalyze);
        
        for(int i : result.keySet()) {
            System.out.println("Column " i " is of type ==> " result.get(i));
        }
        
    }
    
    static String NUMERIC = "NUMERIC";
    static String DATE = "DATE";
    static String STRING = "STRING";
    static String FORMULA = "FORMULA";
    static String BLANK = "BLANK";
    static String BOOLEAN = "BOOLEAN";
    static String ERROR = "ERROR";
    static String NOT_CONSISTENT = "NOT_CONSISTENT";

    public static Map<Integer, String> analyzeColumnTypes(String excelFileName, int sheet_num, int maxRows_ToAnalyze){
        
        Workbook workbook = null;
        Map<Integer, String> columnTypeMap = new HashMap<Integer, String>();
        Map<Integer, Set<String>> tempMap = new HashMap<>();

        Pattern p = Pattern.compile("[^0-9\.]", Pattern.CASE_INSENSITIVE);

        try {
            FileInputStream excelFile = new FileInputStream(new File(excelFileName));
            workbook = new XSSFWorkbook(excelFile);
            Sheet sheet = workbook.getSheetAt(sheet_num);
            int rows_in_sheet = sheet.getPhysicalNumberOfRows();
            
            //if sheet rows are less than number of rows to analyze then analyze all the rows
            if(rows_in_sheet<maxRows_ToAnalyze) {
                maxRows_ToAnalyze = rows_in_sheet;
            }
            
            //i=1 because we dont want to read header rows
            //header rows are typically strings
            for(int i=1; i<=maxRows_ToAnalyze; i  ) {
                
                Row row = sheet.getRow(i);
                int column_count = row.getPhysicalNumberOfCells();

                for(int j=0; j<column_count; j  ) {
                    
                    tempMap.putIfAbsent(j, new HashSet<>());

                    Cell cell = row.getCell(j);
                    
                    if(cell.getCellType().equals(CellType.NUMERIC)) {

                        Matcher m = p.matcher(cell.toString());
                        boolean match = m.find();
                        
                        if(match) {
                            tempMap.get(j).add(DATE);
                        } else {
                            tempMap.get(j).add(NUMERIC);
                        }
                        
                    } else if(cell.getCellType().equals(CellType.STRING)) {
                        tempMap.get(j).add(STRING);
                    } else if(cell.getCellType().equals(CellType.FORMULA)) {
                        tempMap.get(j).add(FORMULA);
                    } else if(cell.getCellType().equals(CellType.BLANK)) {
                        tempMap.get(j).add(BLANK);
                    } else if(cell.getCellType().equals(CellType.BOOLEAN)) {
                        tempMap.get(j).add(BOOLEAN);
                    } else if(cell.getCellType().equals(CellType.ERROR)) {
                        tempMap.get(j).add(ERROR);
                    }
                    
                }
                
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        for(int colnum: tempMap.keySet()) {
            if(tempMap.get(colnum).size()>1) {
                columnTypeMap.put(colnum, NOT_CONSISTENT);
            } else {
                for(String coltype: tempMap.get(colnum)) {
                    columnTypeMap.put(colnum, coltype);
                }
            }
        }

        return columnTypeMap;
    }
}
 

Комментарии:

1. возможно, вам сначала нужно проверить, не является ли ячейка нулевой: if (ячейка == null) { tempMap.get(j).add(ПУСТОЙ); } //или ОШИБКА