C++에서 CSV 파일을 직접 읽고 파싱하는 작업은 생각보다 까다롭습니다. 따옴표로 묶인 필드, 이스케이프 문자, 쉼표가 포함된 값 등 다양한 예외 상황이 존재하기 때문에, 직접 파일을 한 줄씩 처리하면 이러한 경우를 놓치기 쉽습니다. 따라서 실무에서는 검증된 라이브러리를 활용하는 것이 좋습니다.
특히 C++의 Boost 라이브러리는 CSV 파일을 읽고 구문 분석하는 데 매우 유용한 도구들을 제공합니다. 아래에서 Boost를 활용한 방법과 표준 라이브러리만으로 구현하는 방법을 각각 살펴보겠습니다.
방법 1: Boost 라이브러리의 tokenizer 사용
Boost의 tokenizer와 escaped_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와 같은 라이브러리를, 단순한 구조라면 표준 라이브러리만으로 충분히 구현할 수 있습니다.