본문 바로가기

Java

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

반응형

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

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));
    });
}

 

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

반응형