메뉴 닫기

MFC에서 CSV 파일 읽고 쓰기, CStdioFile로 라인 단위 파싱 구현하기


MFC에서 CSV 파일 읽고 쓰기, CStdioFile로 라인 단위 파싱 구현하기

📌 쉼표로 구분된 데이터를 효율적으로 처리하고 싶은 MFC 개발자라면 필독입니다

MFC를 사용해 개발하다 보면, 외부 데이터와 연동하거나 사용자 입력값을 저장할 필요가 있을 때가 많습니다.
그중에서도 CSV(Comma-Separated Values) 형식은 가장 많이 쓰이는 텍스트 기반 데이터 형식 중 하나입니다.
CSV는 단순한 구조 덕분에 엑셀이나 데이터베이스와도 쉽게 호환되며, 소규모 데이터 처리를 자동화할 때 아주 유용하죠.

이번 글에서는 MFC의 CStdioFile 클래스를 활용해 CSV 파일을 한 줄씩 읽고 쓰는 방법부터,
쉼표로 구분된 데이터를 파싱하여 배열로 분리하는 과정까지 상세히 다뤄보겠습니다.
기초적인 파일 입출력부터 실무에서 바로 써먹을 수 있는 코드 예제까지 모두 담았으니, 실전에서 바로 적용해보세요.







🔗 CStdioFile로 CSV 파일 여는 방법

MFC에서 CSV 파일을 다루기 위해 가장 많이 사용하는 클래스는 CStdioFile입니다.
이 클래스는 텍스트 파일을 한 줄씩 읽고 쓰는 기능을 제공하여, 라인 단위 처리가 필요한 CSV에 적합합니다.
특히 CStdioFile은 MFC의 CFile을 상속받은 형태로, 스트림 방식의 입출력에 특화되어 있습니다.

CSV 파일을 열기 위해서는 먼저 CStdioFile 객체를 생성한 후 Open() 메서드를 사용해 파일을 열어야 합니다.
파일 모드는 읽기용이면 CFile::modeRead, 쓰기용이면 CFile::modeWrite | CFile::modeCreate를 사용합니다.

CODE BLOCK
// CSV 파일 읽기 예제
CStdioFile file;
if (file.Open(_T("sample.csv"), CFile::modeRead | CFile::typeText))
{
    CString strLine;
    while (file.ReadString(strLine))
    {
        // 한 줄씩 읽은 결과 처리
    }
    file.Close();
}

위 예제처럼 ReadString() 함수를 이용해 한 줄씩 문자열을 읽어올 수 있으며, 이후 해당 줄을 쉼표 기준으로 파싱하면 됩니다.
파일을 열 때는 항상 예외 처리를 고려하고, 작업이 끝난 후에는 반드시 Close()를 호출해야 합니다.

💡 TIP: 파일 경로는 반드시 유니코드 또는 ANSI 문자열 형식에 맞게 설정해야 하며,
상대 경로보다는 절대 경로를 사용하는 것이 디버깅에 유리합니다.

이처럼 CStdioFile을 통해 CSV 파일을 간단하게 열고, 이후 라인 단위로 데이터를 처리할 수 있는 기반을 마련할 수 있습니다.
다음 단계에서는 라인 단위로 데이터를 읽고 쉼표를 기준으로 파싱하는 과정을 자세히 알아보겠습니다.


🛠️ 라인 단위로 읽기와 파싱 구현

CSV 파일을 한 줄씩 읽은 후에는, 쉼표를 기준으로 데이터를 분리하는 작업이 필요합니다. 이때 사용되는 대표적인 클래스는 CString입니다.
CString은 문자열 처리에 강력한 기능을 제공하여, 쉼표와 같은 구분자를 기준으로 문자열을 분리할 수 있습니다.
다음 예제에서는 CString::Tokenize() 함수를 사용하여 한 줄의 데이터를 쉼표 기준으로 분리하는 방법을 소개합니다.

CODE BLOCK
// 한 줄을 쉼표로 구분하여 파싱
CString strLine = _T("홍길동,25,서울");
CString strToken;
int index = 0;
while ((strToken = strLine.Tokenize(_T(","), index)) != NULL)
{
    // strToken은 쉼표로 구분된 각 항목 (예: "홍길동", "25", "서울")
    AfxMessageBox(strToken);
}

이 예제에서는 Tokenize() 메서드를 사용하여 문자열을 쉼표 기준으로 나누고 있습니다.
Tokenize() 함수는 첫 번째 인자로 구분자를 받으며, 두 번째 인자는 구분된 항목의 인덱스를 지정합니다.
반복문을 사용하여 한 줄에 있는 모든 데이터를 순차적으로 파싱할 수 있습니다.

위 코드에서는 “홍길동,25,서울”이라는 데이터를 쉼표를 기준으로 분리하여 각 항목을 strToken에 저장하고,
순차적으로 AfxMessageBox로 각 항목을 표시하고 있습니다. 이 방식은 다양한 CSV 데이터를 쉽게 파싱할 수 있는 방법입니다.

이제 라인 단위로 CSV 데이터를 읽고, 필요한 필드만 추출하여 적절하게 처리할 수 있는 준비가 되었습니다.
다음에는 이렇게 파싱한 데이터를 처리하는 방식과 파일 쓰기 처리 방법을 살펴보겠습니다.







⚙️ CString으로 쉼표 기준 데이터 분리하기

앞서 살펴본 것처럼 CString::Tokenize()를 활용하면 한 줄의 CSV 데이터를 쉽게 분리할 수 있습니다.
그렇다면 이번에는 이 분리된 데이터를 활용해 어떻게 원하는 값만 필터링하거나 변환할 수 있는지 예제를 통해 알아보겠습니다.

예를 들어, “홍길동,25,서울”이라는 라인을 읽었을 때, 이름과 나이만 사용하고 싶다면 다음과 같이 코드를 구성할 수 있습니다.

CODE BLOCK
// 필요한 항목만 추출
CString strLine = _T("홍길동,25,서울");
CString strToken;
int index = 0;
CString name, age;

int tokenPos = 0;
while ((strToken = strLine.Tokenize(_T(","), index)) != NULL)
{
    if (tokenPos == 0)
        name = strToken;
    else if (tokenPos == 1)
        age = strToken;
    tokenPos++;
}

CString result;
result.Format(_T("이름: %s, 나이: %s"), name, age);
AfxMessageBox(result);

이처럼 각 항목의 순서를 기준으로 필요한 값을 조건문으로 분기하여 추출할 수 있습니다.
토큰 인덱스를 기준으로 특정 필드만 추려낸 뒤, 가공된 형태로 다른 로직에 넘기거나 화면에 출력할 수 있어 매우 유용합니다.

추가로, 데이터가 숫자인 경우에는 _ttoi() 같은 함수를 사용하여 정수형으로 변환해 처리할 수도 있습니다.
이러한 문자열 → 데이터 변환 작업은 CSV 활용에서 자주 사용되므로 익숙해질 필요가 있습니다.

💡 TIP: CSV 파일의 필드 순서가 바뀔 수 있는 경우, 첫 줄을 헤더로 사용해 키-값 매핑 방식으로 처리하면 유지 보수가 쉬워집니다.

이제 문자열 파싱뿐 아니라 데이터 필터링 및 구조화까지 가능해졌습니다.
다음은 이 데이터를 다시 CSV 파일로 저장하는 방법에 대해 설명드리겠습니다.


🔌 CSV 저장을 위한 파일 쓰기 처리

CSV 데이터를 읽고 처리한 후, 이를 다시 파일로 저장하는 방법을 살펴보겠습니다.
CStdioFile 클래스를 사용하여 파일에 데이터를 쓸 때는 WriteString() 메서드를 활용합니다.
이 메서드는 문자열을 파일에 한 줄씩 작성하는 데 유용하며, CSV 형식에 맞게 데이터를 저장할 수 있습니다.

파일을 열 때는 CFile::modeWrite | CFile::modeCreate 플래그를 사용하여 쓰기 모드로 파일을 열고,
파일이 없으면 새로 생성되도록 설정할 수 있습니다. 이제 예제를 통해 CSV 데이터를 파일에 저장하는 방법을 보겠습니다:

CODE BLOCK
// CSV 파일에 데이터 쓰기 예제
CStdioFile file;
if (file.Open(_T("output.csv"), CFile::modeWrite | CFile::modeCreate | CFile::typeText))
{
    // 데이터를 쉼표로 구분하여 작성
    file.WriteString(_T("홍길동,25,서울\n"));
    file.WriteString(_T("김철수,30,부산\n"));
    file.WriteString(_T("이영희,22,대구\n"));
    file.Close();
}

이 예제에서는 WriteString()을 사용해 각 줄을 CSV 형식으로 작성하고 있습니다.
홍길동,25,서울” 형식으로 데이터를 저장하고, 각 항목은 쉼표로 구분되어 있습니다.
작성된 파일은 프로그램이 실행되는 디렉토리에 저장됩니다.

이제 파일에 데이터를 쓸 때 필요한 모든 기본적인 작업을 마쳤습니다.
다음 단계에서는 실제 업무에서 적용할 수 있는 구조 설계 팁에 대해 설명드리겠습니다.
다양한 CSV 데이터를 효율적으로 처리하고, 관리하는 구조를 어떻게 설계할지 알아보겠습니다.







💡 실제 업무에 적용 가능한 구조 설계 팁

CSV 파일을 읽고 쓰는 작업은 단순한 데이터 처리에서부터 복잡한 업무 자동화까지 폭넓게 활용될 수 있습니다.
하지만 업무에서 CSV 파일을 다룰 때는 데이터의 크기나 형식에 따라 보다 효율적인 구조 설계가 필요합니다.
여기에서는 실제 프로젝트에서 쉽게 적용할 수 있는 몇 가지 설계 팁을 공유합니다.

📌 1. 헤더 정보 활용하기

CSV 파일의 첫 번째 줄을 데이터 필드의 헤더로 사용하는 방식은 매우 유용합니다.
헤더를 사용하면 각 필드의 의미를 명확히 정의할 수 있고, 데이터의 순서나 구조가 변경되었을 때 오류를 방지할 수 있습니다.
예를 들어, 첫 줄에 “이름, 나이, 도시”와 같은 필드 이름을 넣어두면, 이후 데이터가 변경되더라도 필드에 대한 명확한 정보를 제공할 수 있습니다.

📌 2. 다양한 데이터 처리 방법 설계하기

CSV 파일에 포함된 데이터가 숫자, 문자열, 날짜 등 다양한 형식일 수 있기 때문에, 이를 적절하게 처리할 수 있는 구조를 설계해야 합니다.
예를 들어, 나이와 같은 숫자 필드는 _ttoi()와 같은 함수를 사용하여 문자열을 숫자로 변환할 수 있고, 날짜 필드는 CTime 클래스를 사용하여 날짜 형식으로 변환할 수 있습니다.
이러한 변환 작업을 구조적으로 관리할 수 있도록 별도의 함수로 처리하면 코드 유지보수성이 높아집니다.

📌 3. 에러 처리와 데이터 검증 강화

CSV 파일을 처리할 때는 파일이 깨지거나 예상치 못한 데이터 형식이 존재할 수 있기 때문에 예외 처리가 중요합니다.
예를 들어, 각 항목이 정확히 구분되어 있는지, 빈 값이 있는지 등을 사전에 체크하여 오류가 발생하지 않도록 처리하는 것이 좋습니다.
또한, 데이터가 올바른 형식인지 검증하는 로직을 추가하여 잘못된 데이터로 인한 오류를 미연에 방지할 수 있습니다.

📌 4. 메모리 사용 최적화

대용량 CSV 파일을 다룰 때는 메모리 사용을 최적화하는 것이 중요합니다.
예를 들어, 전체 데이터를 한 번에 읽어오는 대신, 필요한 데이터만큼씩 읽어오거나 처리하는 방식으로 메모리 부담을 줄일 수 있습니다.
또한, 데이터를 처리한 후에는 더 이상 필요하지 않은 객체나 리소스를 해제하여 메모리 누수를 방지해야 합니다.

이러한 설계 팁을 통해 업무에서의 CSV 파일 처리가 더욱 원활해지고, 코드가 견고하고 확장성 있게 됩니다.
앞으로 CSV 파일을 다루는 프로젝트에서 보다 효율적인 데이터 처리와 구조 설계를 구현할 수 있을 것입니다.


❓ 자주 묻는 질문 (FAQ)

CSV 파일을 처리할 때 성능 문제를 어떻게 해결할 수 있나요?
대용량 CSV 파일을 처리할 때는 데이터를 한 번에 메모리에 올리지 않고, 필요한 데이터만 읽어오는 방식으로 성능을 최적화할 수 있습니다.
또한, 메모리 사용을 최소화하는 방식으로 데이터를 처리하거나, 데이터의 크기가 크다면 멀티스레딩을 활용해 병렬로 처리하는 방법도 고려할 수 있습니다.
CSV 파일을 읽을 때 데이터가 깨지거나 인코딩 문제가 발생하는 경우 어떻게 해결하나요?
CSV 파일을 읽을 때 인코딩 문제가 발생하는 경우, 파일을 열 때 적절한 인코딩 방식을 지정하는 것이 중요합니다.
MFC에서는 파일을 읽을 때 CStdioFile::typeText 옵션을 사용하고, 인코딩을 맞추기 위해 SetCodePage()를 사용해 UTF-8이나 다른 인코딩을 설정할 수 있습니다.
CSV 파일을 저장할 때 특수 문자가 포함된 경우, 어떤 처리를 해야 하나요?
특수 문자가 포함된 CSV 데이터를 저장할 때는, 해당 문자를 적절히 처리하기 위해 큰따옴표로 감싸는 방법이 일반적입니다.
예를 들어, “서울, 가평”과 같은 값은 “서울, 가평”으로 저장하여 CSV 파서가 제대로 처리할 수 있게 합니다. 또한, CSV 파일에서 쉼표나 줄바꿈과 같은 구분자도 제대로 처리할 수 있도록 해야 합니다.
CSV 파일에서 특정 조건에 맞는 데이터만 필터링할 수 있나요?
네, 가능합니다. 데이터를 읽고 파싱한 후, 원하는 조건에 맞는 항목만 필터링하여 새로운 파일에 저장할 수 있습니다.
예를 들어, 나이가 30세 이상인 사람만 필터링하여 새로운 CSV 파일로 저장하는 등의 작업이 가능합니다.
CSV 파일을 처리할 때 빈 데이터나 잘못된 형식은 어떻게 처리해야 하나요?
빈 데이터나 잘못된 형식의 데이터는 예외 처리를 통해 처리할 수 있습니다.
예를 들어, CString::IsEmpty() 또는 _stscanf()을 사용하여 데이터를 검증한 후, 잘못된 데이터는 건너뛰거나 오류 메시지를 출력하여 처리할 수 있습니다.



📌 CSV 파일 처리 정리

이번 글에서는 MFC에서 CSV 파일을 처리하는 방법을 다뤘습니다.
CStdioFile 클래스를 사용하여 파일을 읽고, 라인 단위로 데이터를 파싱하며, 쉼표를 기준으로 각 항목을 처리하는 방법을 살펴보았습니다.
또한, 파일에 데이터를 저장할 때는 WriteString()을 사용하여 CSV 형식으로 저장하는 방법도 알아보았습니다.

CSV 파일을 처리하는 과정에서 중요한 부분은 데이터 검증과 예외 처리입니다.
잘못된 데이터나 예외 상황을 적절히 처리하여 안정적인 프로그램을 만들 수 있습니다.
실제 업무에 적용 가능한 구조 설계 팁을 통해 더 효율적이고 확장성 있는 데이터 처리 방식도 구현할 수 있었습니다.

CSV 파일을 다루는 방법은 매우 기본적이지만, 다양한 업무에서 중요한 역할을 하므로 이번 글을 참고하여
MFC로 CSV 파일을 처리할 때 더욱 능숙하게 다룰 수 있게 되시길 바랍니다.


🏷️ 관련 태그 : CSV파일처리, MFC, CStdioFile, 쉼표구분데이터, 데이터파싱, 파일읽기, 파일쓰기, MFC입출력, 데이터검증, C++