고코딩

JAVA 에서 CSV 파싱하기!! 라이브러리 사용 X 본문

JAVA

JAVA 에서 CSV 파싱하기!! 라이브러리 사용 X

고코딩 2021. 7. 31. 16:45

CSV 파싱 클래스 만들기

회사에서 고도화 프로젝트를 하던 도중 csv파일을 파싱해야할 일이 생겼다. 당연히 csv파일을 파싱하는 라이브러리가 이미 만들어져있었고, 당연히 가져다 사용하려고 했다. 근데 왠걸... 최소 jdk 1.8이상에서만 작동하는 라이브러리였다. 근데 고객사 서버의 자바 버전은 jdk 1.6(아직 까지도 옛날 자바를 쓰는 회사가 많다고 들었지만 실제로 만날 줄 이야...)

결국 csv파일을 파싱하는 코드를 직접 짰다. 근데 짜놓고 나니 왠걸 생각보다 잘 짰었다. 일단 내가 받은 csv파일은 간단한 파일형식이 아니였다. 설명을 시작해보겠다.

, 로 구분하면 되는 거 아니야...?

Csv 파일은 형식이 정해져 있다. CSV(영어: comma-separated values)는 몇 가지 필드를 쉼표(,)로 구분한 텍스트 데이터 및 텍스트 파일이다. 확장자는 .csv이며 MIME 형식은 text/csv이다. comma-separated variables라고도 한다. 예시를 들어보겠다.

1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,j,k
가,나,다,라,마,바,사,아,자,차

파일의 내용이 위와 같다면 , 를 기준으로 데이터를 구분할 수 있다. 이런식으로 데이터를 구분하는 방식이 csv 파일 형식이다. csv는 데이터베이스에서 원하는 데이터를 뽑아서 보낼때 큰 힘으로 작용한다.

만약에 Employ 라는 테이블이 있고, 그 안의 모든 데이터를 SELECT 해서 그 정보를 다른 사람에게 보내줘야한다면 csv형식을 이용해 구할 수 있다.

SELECT EMP_ID, NAME, DEPT_NO FROM EMPLOY 라는 SQL구문의 결과가 아래의 표와 같다면

EMP_ID NAME DEPT_NO
1 홍길동 1001
2 성춘향 2001
3 심봉사 3001

csv형식으로는 아래와 같이 표현한다.

EMP_ID,NAME,DEPT_NO
1,홍길동,1001
2,성춘향,2001
3,심봉사,3001

SQL의 조회 결과를 표현할 때는 첫번째 행에는 컬럼의 이름을 그 아래에는 데이터의 조회 결과를 순서대로 적는다.

만약 결과 값에 null 이 포함되어 있으면

EMP_ID,NAME,DEPT_NO
1,,1001
2,성춘향,
,심봉사,3001

그냥 빈 칸으로 표현한다. 데이터 조회에서 가장 중요한 점은 첫번째 행에서의 데이터의 갯수에 아래의 데이터들의 갯수를 맞춰줘야 한다는 점이다.

근데 데이터를 받아서 보는 도중 애매한 부분이 있었다. 바로 데이터 값 안에 ,가 들어가는 경우이다. 만약 위의 SQL 문에서 주소가 들어간다고 해보자.

EMP_ID NAME DEPT_NO ADDRESS
1 홍길동 1001 서울시, 강남구
2 성춘향 2001 서울시, 송파구
3 심봉사 3001 서울시, 서초구

이럴때는 데이터의 갯수가 모호해 진다. csv는 ,로 데이터를 구분하게 되는데 데이터 값 안에 , 이 들어가는 경우는 어떻게 표현 할까? 아래와 같이 표현 한면 된다.

EMP_ID,NAME,DEPT_NO,ADDRESS
1,홍길동,1001,"서울시, 강남구"
2,성춘향,2001,"서울시, 송파구"
3,심봉사,3001,"서울시, 서초구"

위와 같이 "" 큰 따음표로 감싸주면 된다. 이것은 CSV 의 규칙이다.

그래서 JAVA로 어떻게 읽는데??

솔직히 어느정도 자바를 할 줄 아시는 분들이라면 대충 어떻게 읽는지 알것이다. FileBufferedReader로 한줄씩 String 으로 읽고 .split(",")해주면 된다.

나도 처음에는 이렇게 코딩 했었다. 근데 "," 로 감싼 데이터에서 문제가 발생했다. 자바는 큰따음표를 무시하고 오로지 , 으로만 나누기 때문에 구분을 할 수 가 없었다. 멘붕에 빠졌다. ㅜㅜ

진짜 이 방법 저 방법 다 찾아보다가 정규식을 찾았다.!!(분명히 예전에 배운 내용인데 기억이 안나서 구글링으로 하루동안 삽질했다.ㅜㅜ) 혹시 이 글을 읽으시는 분들은 그냥 내가 쓴 코드를 가져다 쓰시기 바란다. 간단한 코드이고 굳이 여러분들도 나와 같은 고통을 겪을 필요도 없으니...

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

public class GoCSV {
    /*
    * 작성 날짜 : 2021-07-31
    * 작성자    : 고석준
    * 프로그램 요약 :  CSV 파일을 읽거나 쓸 수 있다. jdk 1.6에서는 openCSV 라이브러리가 작동하지 않았기 때문에 내가 스스로 작성하였다.
    * 기능 : 파일 읽어서 리스트로 변환*/

    private String filePath;
    private BufferedReader bufferedReader;
    private List<String[]> readCSV;

    private int index;

    //This constructor is for read CSV File
    public GoCSV(String filePath) throws IOException {
        this.filePath = filePath;
        bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(this.filePath), "UTF-8"));
        readCSV = new ArrayList<>();

        makeList(bufferedReader);
        this.index = 0;
    }

    public void makeList(BufferedReader bufferedReader) throws IOException {
        String line = null;
        while((line = bufferedReader.readLine())!=null) {
            String[] lineContents = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)",-1);

            readCSV.add(lineContents);
        }
    }

    //한 행을 읽음
    public String[] nextRead(){
        if(readCSV.size() == index){
            return null;
        }
        return readCSV.get(index++);
    }
}

위의 클래스를 추가해서 사용하면 된다. 사용 예제는 아래에 적어 놓았다.

GoCSV goCSV = new GoCSV("/Users/ksj/Desktop/sample.txt");
        String[] line=null;
        while((line = goCSV.nextRead())!=null){
            for(String a : line){
                System.out.print(a +" ");
            }
            System.out.println();
        }
스크린샷 2021-07-31 오후 4 40 01 스크린샷 2021-07-31 오후 4 42 03

위 와 같이 쓰면 된다.


분명히 부족한 코드이다. 첫 작성이기도 하고 원래 계획은 라이브러리로 만들어서 배포하려고 했다. 기능도 더 추가해야 한다. 근데 처음 써보는 제대로 된 내 글이여서 여기까지만 써야겠다. 내 능력부족이다 ㅜㅜ. 나중에 더 써야겠다