Java

OpenCSV로 쌍따옴표(Double quote)와 개행(Carriage return)이 포함된 CSV 파싱하기

movingJin 2022. 5. 31. 13:50
반응형

포스팅 제목부터 범상치 않은데요,

CSV파일을 다루면서 파싱하는데 어려움이 있던 부분을 포스팅합니다.

Java OpenCSV 라이브러리를 사용했구요,

Gradle에서 아래와 같이 추가하면 됩니다.

implementation 'com.opencsv:opencsv:5.3'

문제의 CSV본문은 사람이 입력한 자연어 그대로 들어가기에 파싱되는 필드내에 콤마(,)와 쌍따옴표("), 개행(\n)까지 들어있을 수 있습니다.

예시는 아래와 같습니다.

 

"1","김철수","일반회원","교정진료를 ""희망""합니다, 잘부탁드립니다."

"2","홍길동","일반회원","스케일링을

희망합니다, 1달에 2번"

 

예시를 보면 쌍따옴표로 묶인 필드 내에 콤마와 개행, 심지어 쌍따옴표까지 혼합해서 나온 경우를 보실 수 있습니다.

상당히 까다로워 보이지만, 저 예시는 CSV포맷을 지키고 있습니다.

즉, 파싱가능한 문장이란거죠.

 

코드를 보겠습니다.

private boolean isArmsETLSuccess(String csvFilePath) {
    boolean ret = true;

    List<Arms> armsList = null;
    try (BufferedReader br = Files.newBufferedReader(Path.of(csvFilePath), StandardCharsets.UTF_8)) {
        ColumnPositionMappingStrategy<Arms> strategy = new ColumnPositionMappingStrategy<>();
        String[] columns = Arms.getFieldNames(1, 30).toArray(String []:: new);
        strategy.setColumnMapping(columns);
        strategy.setType(Arms.class);

        CsvToBean<Arms> csvToBean = new CsvToBeanBuilder<Arms>(br)
                .withMappingStrategy(strategy)
                .withSkipLines(1)
                .withStrictQuotes(true)
                .withEscapeChar('\n')
                .withIgnoreLeadingWhiteSpace(true)
                .build();

        csvToBean.setThrowExceptions(false);
        armsList = csvToBean.parse();
        List<CsvException> exceptionList = csvToBean.getCapturedExceptions();
        if(!exceptionList.isEmpty()){
            ret = false;
            exceptionList.forEach(e -> {
               log.error(String.format("dirPath:%s, CSV parse error:", csvFilePath), e);
        }
        for(Arms arms: armsList){
			
        }
        armsRepository.saveAll(armsList);
    } catch (IOException | java.lang.RuntimeException e) {
        ret = false;
        log.error(String.format("dirPath:%s, CSV parse error:", csvFilePath), e);
    }

    return ret;
}

예시의 코드는 CSV파일을 파싱해서 Arms라는 객체로 저장하는 예시입니다.

손으로 일일이 짜려면 코드가 복잡해지지만, OpenCSV에선 CsvToBean 클래스를 지원해줘서

용자가 정의한 DTO로 변환이 손쉽게 됩니다.

 

파싱하려는 예시가 쌍따옴표로 묶여있기 때문에

.withStrictQuotes(true)

로 필드가 묶여있다는걸 명시하고,

 

.withEscapeChar('\n')

로 파싱된 필드내에 개행이 있는 경우, 새로운 레코드가 아님을 예외처리 합니다.

 

.withIgnoreLeadingWhiteSpace(true)

필드에 공백이 있을 수 있으므로 공백도 예외처리 해둡니다.

 

 

만약, 파싱중에 에러를 핸들링하고 싶다면,

parse 메서드를 호출하기 전에

csvToBean.setThrowExceptions(false);

를 설정해서 exception이 발생해도 파싱을 멈추지않고 모아두어 아래처럼 나중에 처리할 수 있습니다.

List<CsvException> exceptionList = csvToBean.getCapturedExceptions();
if(!exceptionList.isEmpty()){
    ret = false;
    exceptionList.forEach(e -> {
        saveVocExceptionLog(
                csvFilePath,
                e.getLineNumber(),
                String.join(",", e.getLine()),
                ARMS,
                ExceptionUtils.getStackTrace(e));
    });
}

 

이상으로 포스팅 마치겠습니다!

반응형