Development/Daily Error

[POI] 엑셀의 숫자를 소수점으로 파싱하는 문제 해결하기

📝 작성 : 2022.08.25  ⏱ 수정 : 
반응형

문제

POI 를 활용해 엑셀파일의 각 셀에 있는 값들을 자바에서 읽어 오는 과정에서 문제가 있었습니다.

기존의 코드는 아래와 같습니다.

switch (cell.getCellType()) {
    case FORMULA:
        value = cell.getCellFormula();
        break;
    case NUMERIC:
        value = String.valueOf(cell.getNumericCellValue());
        break;
    case STRING:
        value = cell.getStringCellValue();
        break;
    case BOOLEAN:
        value = String.valueOf(cell.getBooleanCellValue());
        break;
    default:
}

각각의 셀 타입에 따라서 적절한 스트링으로 변환 하는 과정인데요.

이렇게 해서 값을 읽어 오면, 예를 들어 셀에 100이라는 값이 있으면 100.0 으로 넘어오는 문제가 있었습니다.

원인

image-20220824151849015

getNumericCellValue() 메서드가 double로 값을 반환하기 때문에 그걸 String으로 변환 하는 과정에서 자연스럽게 소수점이 붙어서 캐스팅 되고 있었습니다.

해결

해결 방법은 여러가지가 있는데요, 천천히 근본적인 해결 방법에 다가가겠습니다.

타입 캐스팅

일단, 소수점을 쓸 일이 없다면 단순하게 int로 캐스팅 해 버리면 됩니다.

value = String.valueOf((int)cell.getNumericCellValue());

이렇게 되면 String.valueOf의 파라미터가 int 타입이기 때문에 소수점 문제가 발생하지 않습니다.

하지만.. 소수점도 입력을 받을 수 있는 구조여야 한다면 문제가 됩니다.

Integer인지 체크

이때는 변환 전에 해당 double 타입의 값이 int 값인지 확인 하고, 그렇다면 캐스팅을 하고 그렇지 않다면 캐스팅을 하지 않는 방법이 가능 합니다.

case NUMERIC:
double numericCellValue = cell.getNumericCellValue();
value = String.valueOf(numericCellValue);
if (DoubleMath.isMathematicalInteger(numericCellValue)) {
    value = String.valueOf((int) numericCellValue);
}
break;

구글 구아바의 DoubleMath.isMathematicalInteger 메서드를 활용하면 손쉽게 integer 값인지를 확인 할 수 있는데요, true 일 경우에만 캐스팅을 해 주면 의도대로 잘 동작 합니다.

Guava 없이

Guava 를 사용하면 해당 라이브러리에 대한 의존이 필요하기 때문에 다른 방법으로 코드를 개선 해 보겠습니다.

case NUMERIC:
    double cellValue = cell.getNumericCellValue();
    if (cellValue == Math.rint(cellValue)) {
        value = String.valueOf((int) cellValue);
    } else {
        value = String.valueOf(cellValue);
    }
    break;

Math.rint를 이용하는 방법 인데요.

image-20220824152910080

가장 가까운 int 값을 찾아 double 형태로 변환 해 주는 메서드 인데, 이를 활용해서 찾은 값이 기존의 dobule 값과 동일하다면 기존의 값이 int value 라는게 증명 됩니다.

이렇게 하면, Guava를 굳이 이용하지 않고도 구현이 가능합니다.

이상입니다.

반응형