Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++에서 CSV 파일을 읽고 구문 분석하는 방법

C++에서 CSV 파일을 직접 읽고 파싱하는 작업은 생각보다 까다롭습니다. 따옴표로 묶인 필드, 이스케이프 문자, 쉼표가 포함된 값 등 다양한 예외 상황이 존재하기 때문에, 직접 파일을 한 줄씩 처리하면 이러한 경우를 놓치기 쉽습니다. 따라서 실무에서는 검증된 라이브러리를 활용하는 것이 좋습니다.

특히 C++의 Boost 라이브러리는 CSV 파일을 읽고 구문 분석하는 데 매우 유용한 도구들을 제공합니다. 아래에서 Boost를 활용한 방법과 표준 라이브러리만으로 구현하는 방법을 각각 살펴보겠습니다.

방법 1: Boost 라이브러리의 tokenizer 사용

Boost의 tokenizerescaped_list_separator를 사용하면 이스케이프 문자와 따옴표 처리까지 자동으로 수행해 주기 때문에 안정적으로 CSV 라인을 파싱할 수 있습니다.

#include<iostream>
vector<string> parseCSVLine(string line){
   using namespace boost;

   std::vector<std::string> vec;

   // 입력 문자열을 토큰으로 분리
   tokenizer<escaped_list_separator<char> > tk(line, escaped_list_separator<char>
   ('\\', ',', '\"'));
   for (auto i = tk.begin();  i!=tk.end();  ++i)
   vec.push_back(*i);

   return vec;
}

int main() {
   std::string line = "hello,from,here";
   auto words = parseCSVLine(line);
   for(auto it = words.begin(); it != words.end(); it++) {
      std::cout << *it << std::endl;
   }
}

출력 결과

위 코드를 실행하면 다음과 같은 결과가 출력됩니다.

hello
from
here

escaped_list_separator<char>에 전달되는 세 개의 인자는 순서대로 이스케이프 문자(\\), 필드 구분자(,), 따옴표(")를 의미합니다. 이 설정 덕분에 복잡한 CSV 형식도 비교적 안전하게 처리할 수 있습니다.

방법 2: getline 함수와 커스텀 구분자 사용

외부 라이브러리 없이 표준 라이브러리만으로 해결하고 싶다면, getline 함수에 원하는 구분자(delimiter)를 지정하여 문자열을 분리하는 방법을 사용할 수 있습니다.

#include <vector>
#include <string>
#include <sstream>

using namespace std;

int main() {
   std::stringstream str_strm("hello,from,here");
   std::string tmp;
   vector<string> words;
   char delim = ','; // 문자열을 나눌 구분자 정의

   while (std::getline(str_strm, tmp, delim)) {
      // tmp가 비어 있는지 확인하는 등의 유효성 검사 추가 가능
      // !, ., ? 같은 특수 기호를 제거하는 처리도 여기서 수행
      // 최종적으로 벡터에 저장
      words.push_back(tmp);
   }

   for(auto it = words.begin(); it != words.end(); it++) {
      std::cout << *it << std::endl;
   }
}

출력 결과

이 코드 역시 동일한 결과를 출력합니다.

hello
from
here

두 방법의 차이점

Boost tokenizer 방식은 따옴표로 묶인 필드나 이스케이프된 문자가 포함된 복잡한 CSV 데이터를 처리할 때 유리합니다. 반면 getline 방식은 외부 의존성 없이 간단한 CSV를 빠르게 처리할 수 있다는 장점이 있지만, 따옴표 내부에 구분자가 포함된 경우에는 추가적인 처리 로직이 필요할 수 있습니다.

따라서 프로젝트 요구 사항에 따라 적절한 방법을 선택하시기 바랍니다. 데이터 형식이 복잡하다면 Boost와 같은 라이브러리를, 단순한 구조라면 표준 라이브러리만으로 충분히 구현할 수 있습니다.