poi excel数字のインポート精度に問題が発生

5174 ワード

今日のテストフィードバックでは、excelでデータをインポートする際に精度の問題が発生しました.例えば、excelの4.6が4.599999999996,4.4に4億4000万1,000万1になったなどです.
私の元のコードはこうでした
public static Object getCellFormatValue(Cell cell) throws ParseException{
        Object cellValue = null;
        if(cell!=null){
            // cell 
            switch(cell.getCellType()){
            case Cell.CELL_TYPE_NUMERIC:{
            	// cell 
                if(DateUtil.isCellDateFormatted(cell)){
                    // YYYY-mm-dd
                	Date date = cell.getDateCellValue();
                	SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                	cellValue = sdf.format(date);
                }else{
                	cell.setCellType(Cell.CELL_TYPE_STRING);
                    cellValue = cell.getRichStringCellValue().getString();
                    // 
                    //cellValue = String.valueOf(cell.getNumericCellValue());
                }
                break;
            }
            case Cell.CELL_TYPE_FORMULA:{
                cellValue = String.valueOf(cell.getNumericCellValue());
                break;
            }
            case Cell.CELL_TYPE_STRING:{
            	cellValue = cell.getRichStringCellValue().getString();
                break;
            }
            default:
                cellValue = "";
            }
        }else{
            cellValue = "";
        }
        return cellValue;
    }

タイプはCELL_ですTYPE_NUMERICの場合、日付タイプでなければ強制的に文字列タイプに変換され、debug後にこのステップで精度の問題が発生していることがわかり、背後の原理が少し理解できず、対応する文字列を直接返せばいいのに.
試してみるとcellを使う.getNumericCellValue()は、正しい値4.6を返します.しかし、これはまた一つの問題をもたらします.poiは数字をdouble型に変えます.例えば4が4.0になり、導入が間違っています.その時、cellを先に使わないでほしいです.getStringCellValue()は、文字列を返して小数点があるか否かを判断し、小数点がある場合はcellを用いる.getNumericCellValue()はdoubleタイプを返します
public static Object getCellFormatValue(Cell cell) throws ParseException{
        Object cellValue = null;
        if(cell!=null){
            // cell 
            switch(cell.getCellType()){
            case Cell.CELL_TYPE_NUMERIC:{
            	// cell 
                if(DateUtil.isCellDateFormatted(cell)){
                    // YYYY-mm-dd
                	Date date = cell.getDateCellValue();
                	SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                	cellValue = sdf.format(date);
                }else{
                	cell.setCellType(Cell.CELL_TYPE_STRING);
                    cellValue = cell.getStringCellValue();
                    if(((String)cellValue).indexOf(".") > -1){
                         cell.setCellType(Cell.CELL_TYPE_NUMERIC);
                         cellValue = String.valueOf(cell.getNumericCellValue());
                    }
                }
                break;
            }
            case Cell.CELL_TYPE_FORMULA:{
                cellValue = String.valueOf(cell.getNumericCellValue());
                break;
            }
            case Cell.CELL_TYPE_STRING:{
            	cellValue = cell.getRichStringCellValue().getString();
                break;
            }
            default:
                cellValue = "";
            }
        }else{
            cellValue = "";
        }
        return cellValue;
    }

しかしこの使い方はだめで、cellのgetメソッドを呼び出すとcellの値が変わり、非常におかしく、理解できず、論理的にも不合理で、getメソッドはオブジェクト内部の属性を変えるべきではない.
例えば、元の値が4.6である、getStringCellValueが呼び出されてからgetNumericCellValueが19.0となる.元の値が4.4であれば21.0になり、具体的な論理も分からない.
要するにcellのgetメソッドは1回しか呼び出せません.私が困っている間に、debug時にcellに移動すると4.6という数字が直接表示されることに気づきました.それはcellのtoString方法がセルの内容を直接表示できることを意味しているのではないでしょうか.やってみたらcelltoStringメソッドはcellTypeでCELL_TYPE_NUMERICの場合、getNumericCellValueメソッドに相当し、数値もdoubleタイプに変換されます.ただし、toStringメソッドを呼び出すとcellの値は変更されません.
public static String getCellFormatValue(Cell cell) throws ParseException{
        String cellValue = null;
        if(cell!=null){
            // cell 
            switch(cell.getCellType()){
            case Cell.CELL_TYPE_NUMERIC:{
            	// cell 
                if(DateUtil.isCellDateFormatted(cell)){
                    // YYYY-mm-dd
                	Date date = cell.getDateCellValue();
                	SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                	cellValue = sdf.format(date);
                }else{
                	String cellstr = cell.toString();
                	cell.setCellType(Cell.CELL_TYPE_STRING);
                	cellValue = cell.getStringCellValue();
                	if(cellValue.indexOf(".") > -1) {
                		cellValue = cellstr;
                	}
                }
                break;
            }
            case Cell.CELL_TYPE_FORMULA:{
                cellValue = String.valueOf(cell.getNumericCellValue());
                break;
            }
            case Cell.CELL_TYPE_STRING:{
            	cellValue = cell.getStringCellValue();
                break;
            }
            default:
                cellValue = "";
            }
        }else{
            cellValue = "";
        }
        return cellValue;
    }

そして大功を成し遂げた