본문 바로가기
JAVA공부/0-자바기초(초급자)

[FIle I/O] 자바로 CSV 파일을 읽고 생성하기 / CSV파일을 자바의 자료구조로 저장, 자바의 자료구조를 CSV 파일로 생성하는 방법을 알아보자

by 으노으뇨 2023. 12. 4.
728x90
반응형
SMALL

안녕하세요!~~

이번엔 파일구조와 자바의 자료구조에 대해서 학습하고자 

자바로 CSV파일을 읽고 자료구조로 저장, 자료구조로 CSV 파일 생성

자바를 처음 공부하는 수준의 저에겐 자바의 자료구조와 파일에 대해서 조금 공부할 수 있는 기회가 되었습니다.


주로 사용된 기술

  1. 파일 입출력 (File I/O) 기술
    BufferedReader, BufferedWriter를 사용하여 파일을 읽고 쓰는 기능이 있습니다. 파일에서 데이터를 읽을 때 InputStreamReader를 사용해 인코딩을 지정하고, 데이터를 쓸 때 OutputStreamWriter로 동일한 방식을 사용하고 있습니다.
  2. 자료구조 활용
    List, Map을 활용하여 CSV 파일 데이터를 저장하고 관리합니다. CSV 파일의 각 라인은 Map<String, Object>로 저장되고, 이러한 Map들을 List에 저장합니다.
  3. 파일 확인 및 생성
    파일의 존재 여부를 확인하는 fileExists 메서드와 새 파일을 생성하는 createNewFile 메서드가 있습니다.
  4. CSV 데이터 파싱
    CSV 파일의 데이터를 파싱하여 Map 형태로 변환하는 로직이 있습니다. 각 라인의 데이터를 쉼표(,)로 분할하여 Map에 저장하고, 이를 List에 추가합니다.

학습성과

1. 파일 처리
  • 파일 경로 다루기: java.io.File 클래스를 사용하여 파일 경로를 설정하고, 해당 디렉토리 내의 파일 목록을 읽는 방법을 익힐 수 있습니다.
  • 파일 읽기: FileInputStream, InputStreamReader, BufferedReader를 활용하여 파일을 읽고 데이터를 처리하는 방법을 이해할 수 있습니다.
  • 파일 쓰기: FileOutputStream, OutputStreamWriter, BufferedWriter를 사용하여 파일에 데이터를 쓰는 방법을 익힐 수 있습니다.
2. 자료 구조 활용
  • List, Map과 같은 자바의 자료구조를 활용하여 데이터를 저장하고 관리하는 방법을 익힐 수 있습니다.
  • CSV 파일 데이터를 자료구조에 적절히 담아 처리하고 이를 출력하는 방법을 학습할 수 있습니다.

자바의 입출력 기능을 활용하여 파일을 읽고 쓰는 방법을 이해할 수 있었고, 가장큰 성과는 테이블형식의 자료구조를 이용하고 이를 응용하여 CSV 파일을 읽고 출력하고 생성할 수 있었습니다.


상황

파일 구조

  • CSV 폴더 안에 동작하는 소스 CSVHandler 파일 , 그리고 csv 파일이 저장되어있고, 또 csv 파일들은 2개가 있습니다.

  • Storage 폴더안에 있는 csv 파일은 위와 같습니다. 왼쪽 : SystemTable.csv, 오른쪽 : SubWayTable.csv 
  • 이 소스들을 자바소스에서 불러와서 자바의 자료구조로 담아낸다.
  • 자바의 자료구조로 담아냈다면 출력을 통해 데이터를 확인한다.
  • SubWayTable.csv 의 내용을 새로운 자료구조로 담고, 새로운 CSV 파일 생성때 참조데이터로 사용한다.

전체소스

package Java.File.CSV;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CSVHandler {
	private static final String FILE_PATH = "C:\\Users\\zhfld\\git\\Study_java\\src\\Java\\File\\CSV\\Storage";

	public static void main(String[] args) {
		Map<String, List<Map<String, Object>>> csvData = readCSVFiles(FILE_PATH);
		for (Map.Entry<String, List<Map<String, Object>>> entry : csvData.entrySet()) {
			System.out.println(entry.getKey() + ": ");
			for (Map<String, Object> vals : entry.getValue()) {
				for (String key : vals.keySet()) {
					System.out.print(vals.get(key) + "\t");
				}
				System.out.println();
			}
		}

		writeCSV(FILE_PATH + "\\NewCsvFile.csv", csvData.get("SubWayTable.csv"));
	}

	private static Map<String, List<Map<String, Object>>> readCSVFiles(String folderPath) {
		Map<String, List<Map<String, Object>>> csvData = new HashMap<>();
		File folder = new File(folderPath);
		File[] listOfFiles = folder.listFiles();

		if (listOfFiles != null) {
			for (File file : listOfFiles) {
				if (file.isFile() && file.getName().endsWith(".csv")) {
					List<Map<String, Object>> dataList = readCSV(file);
					csvData.put(file.getName(), dataList);
				}
			}
		}
		return csvData;
	}

	private static List<Map<String, Object>> readCSV(File file) {
		List<Map<String, Object>> dataList = new ArrayList<>();
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "cp949"))) {
			String line;
			String[] headers = null;
			while ((line = reader.readLine()) != null) {
				if (headers == null) {
					headers = line.split(",");
					continue;
				}
				String[] values = line.split(",");
				Map<String, Object> data = new HashMap<>();
				for (int i = 0; i < headers.length; i++) {
					data.put(headers[i], values[i]);
				}
				dataList.add(data);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return dataList;
	}

	private static void writeCSV(String filePath, List<Map<String, Object>> dataList) {
		if (!fileExists(filePath)) {
			createNewFile(filePath);
		}
		try (BufferedWriter writer = new BufferedWriter(
				new OutputStreamWriter(new FileOutputStream(filePath, true), "cp949"))) {
			if (!dataList.isEmpty()) {
				Map<String, Object> firstData = dataList.get(0);
				StringBuilder header = new StringBuilder();
				for (String key : firstData.keySet()) {
					header.append(key).append(",");
				}
				header.deleteCharAt(header.length() - 1);
				writer.write(header.toString());
				writer.newLine();
				for (Map<String, Object> data : dataList) {
					StringBuilder line = new StringBuilder();
					for (Object value : data.values()) {
						line.append(value).append(",");
					}
					line.deleteCharAt(line.length() - 1);
					writer.write(line.toString());
					writer.newLine();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static boolean fileExists(String filePath) {
		File file = new File(filePath);
		return file.exists();
	}

	private static void createNewFile(String filePath) {
		try {
			File file = new File(filePath);
			if (file.createNewFile()) {
				System.out.println("New file created: " + file.getName());
			} else {
				System.out.println("File already exists.");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

소스설명

CSV 파일을 읽어오는 함수 : readCSVFiles 
private static Map<String, List<Map<String, Object>>> readCSVFiles(String folderPath) {
    Map<String, List<Map<String, Object>>> csvData = new HashMap<>();
    File folder = new File(folderPath);
    File[] listOfFiles = folder.listFiles();

    if (listOfFiles != null) {
        for (File file : listOfFiles) {
            if (file.isFile() && file.getName().endsWith(".csv")) {
                List<Map<String, Object>> dataList = readCSV(file);
                csvData.put(file.getName(), dataList);
            }
        }
    }
    return csvData;
}
private static final String FILE_PATH = "C:\\Users\\zhfld\\git\\Study_java\\src\\Java\\File\\CSV\\Storage";

위와같이 하드코딩으로 우선 filePath를 가져와서 해당 경로에 있는 csv 파일을 모두 가져옵니다. 그리고 Map 자료형에 해당 csv 데이터를 넣습니다.

CSV 파일을 자료형으로 변환하는 함수 : readCSV
private static List<Map<String, Object>> readCSV(File file) {
    List<Map<String, Object>> dataList = new ArrayList<>();
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "cp949"))) {
        String line;
        String[] headers = null;
        while ((line = reader.readLine()) != null) {
            if (headers == null) {
                headers = line.split(",");
                continue;
            }
            String[] values = line.split(",");
            Map<String, Object> data = new HashMap<>();
            for (int i = 0; i < headers.length; i++) {
                data.put(headers[i], values[i]);
            }
            dataList.add(data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return dataList;
}

이 코드는 CSV 파일을 읽어들여 각 행의 데이터를 Map형태로 가공하여 리스트에 저장하는 메서드입니다. 이 코드를 통해 데이터 구조활용과 문자 인코딩 처리를 학습할 수 있었습니다.

List<Map<String, Object>>를 사용하여 데이터를 저장하는 방법을 익힐 수 있습니다. 각 Map은 CSV 파일의 한 행을 나타내며, 열의 이름과 값을 쌍으로 가지고 있습니다.

HashMap을 사용하여 데이터를 열 이름과 값의 쌍으로 저장하고, ArrayList를 사용하여 이러한 데이터를 리스트에 추가하는 방법을 배울 수 있습니다.

그리고 InputStreamReader를 사용하여 파일의 문자 인코딩을 지정할 수 있습니다. 이를 통해 다양한 문자 인코딩을 다루고 파일을 올바르게 해석하는 방법을 배울 수 있습니다.

CSV 파일을 생성하는 함수 : writeCSV
private static void writeCSV(String filePath, List<Map<String, Object>> dataList) {
    if (!fileExists(filePath)) {
        createNewFile(filePath);
    }
    try (BufferedWriter writer = new BufferedWriter(
            new OutputStreamWriter(new FileOutputStream(filePath, true), "cp949"))) {
        if (!dataList.isEmpty()) {
            Map<String, Object> firstData = dataList.get(0);
            StringBuilder header = new StringBuilder();
            for (String key : firstData.keySet()) {
                header.append(key).append(",");
            }
            header.deleteCharAt(header.length() - 1);
            writer.write(header.toString());
            writer.newLine();
            for (Map<String, Object> data : dataList) {
                StringBuilder line = new StringBuilder();
                for (Object value : data.values()) {
                    line.append(value).append(",");
                }
                line.deleteCharAt(line.length() - 1);
                writer.write(line.toString());
                writer.newLine();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

 

이 소스는 CSV 파일을 작성하는 메서드로, 리스트로 제공된 데이터를 받아 파일로 저장합니다. 여기서 엄청난 고생을 했습니다.

파일이 존재하지 않는 경우에만 새 파일을 생성하고 있습니다. 기존 파일이 있는 경우에는 기존 파일에 데이터를 추가합니다.

우선 파일 쓸때 BufferedWriter를 사용하여 파일에 데이터를 쓰고 있습니다. 이를 통해 파일 작성 시 버퍼링을 수행하여 I/O 성능을 향상시킬 수 있습니다.

OutputStreamWriter를 사용하여 파일을 'cp949' 인코딩으로 작성하고 있습니다. 이는 한글을 포함한 문자를 지원하기 위한 것입니다.

주어진 List<Map<String, Object>> 형태의 데이터를 CSV 파일 형식으로 변환하여 저장하고 있습니다. 각 Map은 CSV 파일의 한 행을 나타내며, 열의 이름과 값을 쌍으로 가지고 있습니다.

첫 번째 데이터의 키를 CSV 파일의 헤더로 사용하여 열 이름을 작성합니다. 그 후, 각 행의 값을 CSV 형식에 맞게 작성하여 파일에 씁니다.


실행 결과

실행결과입니다. 

csv의 파일이름과 해당 파일의 데이터가 빠짐없이 모두 출력되었으며

맨 하단 파일이 성공했을때 나오는 문구도 정상적으로 출력됨에 따라 파일이 생성성공했음을 알 수 있습니다.

실제로 NewCsvFile.csv 파일이 생성되었습니다.
구성도 SubWayTable.csv 파일을 참조했으므로 동일합니다.


마치며

이 코드를 통해 CSV 파일을 작성하는 데 필요한 기본적인 파일 입출력과 데이터 처리 방법을 학습할 수 있습니다. 또한, 문자 인코딩과 자료구조와 같은 프로그래밍의 중요한 측면도 경험할 수 있습니다. 이는 실제로 파일을 다루고 데이터를 가공하는 데 아주 작은 기능과 요소중 하나이지만 차근차근 학습해 나가면서 초보 개발자에서 탈줄하고자 계속 나아가야 겠다고 결심했습니다.

728x90
반응형
LIST

댓글