CSV 읽기

programming/general 2007/01/17 03:32
수치데이터들을 엑셀등에서 가볍게 작업해서 사용할때, CSV 포맷이 편하고 좋습니다. 구조가 간단해서 다루기 쉽고, 데이터를 입력하는 입장에서도 아주 불편하지는 않고.

옛날에 정리한거 간단하게 올려봅니다.

CSV 기본룰

- 엑셀에서 작업한 파일은 CSV (쉼표로 분리) (*.csv)로 저장할 수 있다.
- 행은 다음줄 라인피드(\n)로, 열은 콤마 (,)로 구분한다. (탭 등으로 대체 가능)
- 데이타는 따옴표(") 로 둘러 싸일 수 있으며 따옴표안의 따옴표는 두개의 연속된 따옴표로 구성된다.
- 구분자가 아닌 내용중의 쉼표는, 따옴표로 둘러싸인 데이타로 표현된다.
- 전체 행,열의 크기, 타입등의 부가적인 정보는 없다.

샘플시트와 CSV



위와 같은 시트면 다음과 같이 저장됩니다.

1,2,3,4
"4,","5""",,4

샘플코드(의미만...)

#include <string.h>
#include <stdio.h>

class    CCSVReader
{
public :
  CCSVReader();
  ~CCSVReader();

  bool Load(const char *fname);

  int GetData(int col, int row, char * outptr, int outlen) ;

private :
  char * m_pBuffer;
  int * m_pOffset;
  int m_iLine;

  int CountLine(const char *buffer, int * offset);
  int PassToken(const char * str);
  char * ReadFile(const char *fname);
} ;

  CCSVReader :: CCSVReader()
{
  m_pBuffer = NULL;
  m_pOffset = NULL;
  m_iLine = 0;
}

  CCSVReader :: ~CCSVReader()
{
  if (m_pBuffer)
       delete [] m_pBuffer;
  if (m_pOffset)
       delete [] m_pOffset;
}

char * CCSVReader :: ReadFile(const char *fname)
{
  FILE *fp;
  char * ptr;
  int len;

  fp = fopen(fname, "rb");
  if (fp == NULL)
       return NULL;
  fseek(fp, 0, SEEK_END);
  len = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  ptr = new char [len+1];
  fread(ptr, 1, len, fp);
  ptr[len] = '\0';
  fclose(fp);

  return ptr;
}

bool CCSVReader :: Load(const char *fname)
{
//    파일 전체를 읽는다
//
  m_pBuffer = ReadFile(fname);

//    라인수를 센 후 각 오프셋을 구한다
//
  m_iLine = CountLine(m_pBuffer, NULL);
  m_pOffset = new int [m_iLine];
  CountLine(m_pBuffer, m_pOffset);

  return true;
}

int CCSVReader :: CountLine(const char *buffer, int * offset)
{
  const char * p = buffer;
  int line;

  for(line=0; p; line++)
  {
       if (line > 0)
           p++;

       if (offset)
           offset[line] = (int)(p - buffer);

       p = strchr(p, '\n');
  }

  return line;
}

int CCSVReader :: PassToken(const char * str)
{
  int i = 0;

  if (str[0] == '\"')
  {
       for(i=1; ;)
       {
           if (str[i] == '\"')
           {
               if (str[i+1] == '\"')
                   i += 2;
               else
                   break;
           }    else
               i += 1;
       }
       return i+1;
  }    else
  {
       for(i=0; !strchr(",\n", str[i]); i++) ;
       return i;
  }
}

int CCSVReader :: GetData(int col, int row, char * outptr, int outlen)
{
  int i, off;

  if (row >= m_iLine)
       return 0;

  for(i=0, off=m_pOffset[row]; i<col; off++, i++)
  {
       off += PassToken(m_pBuffer + off);
       if (strchr("\n", m_pBuffer[off]))
       {
           outptr[0] = '\0';
           return 0;
       }
  }

  if (m_pBuffer[off] == '\"')
  {
       for(i=0, off+=1; ; i++)
       {
           if (m_pBuffer[off] == '\"' && m_pBuffer[off+1] == '\"')
           {
               if (i < outlen-1)
                   outptr[i] = m_pBuffer[off];
               off += 2;
           }    else
           if (m_pBuffer[off] != '\"')
           {
               outptr[i] = m_pBuffer[off];
               off += 1;
           }    else
               break;
       }
  }    else
  {
       for(i=0; !strchr(",\n", m_pBuffer[off]); i++, off++)
       {
           if (i < outlen-1)
               outptr[i] = m_pBuffer[off];
       }
  }

  if (i >= outlen-1)
       outptr[outlen-1] = '\0';
  else
       outptr[i] = '\0';
  return i;
}

#include <stdlib.h>

void main()
{
  CCSVReader reader;
  char data[32];
  int t = 0;
  reader.Load("Book1.csv");
  while(t++ < 10000) {
       reader.GetData(rand()%100, rand()%100, data, (rand()%(sizeof(data)/2) + sizeof(data)/2)); // 별의미없음 ^^
  }
}


댓글을 달아 주세요

  1. 꼬룡츈 2007/01/17 09:59  댓글주소  수정/삭제  댓글쓰기

    csv에서 ,가 내용중에 실수로 들어가거나,
    뒤쪽으로 ,,,,,,,,, 이런게 붙게 되었을때의
    예외처리 같은것도 필요하더라구요^-^

    • noerror 2007/01/17 10:58  댓글주소  수정/삭제

      ,가 내용중에 있을 경우 따옴표로 감싸줘서 괜찮았던거 같은데(내용처럼), 한번 확인해봐야 겠네요.

  2. 꼬룡츈 2007/01/19 16:59  댓글주소  수정/삭제  댓글쓰기

    엑셀에서 작업해도 ,가 내용에 들어가면 ""가 감싸는군요 ^^;
    ""로 감싸져있는 부분을 예외처리 하니까 원활하게 돌아가는군요~
    읔; 짧은 지식이 누가 됐네요^^;

  3. 차형 2007/02/01 15:06  댓글주소  수정/삭제  댓글쓰기

    근데.. 엑셀에서는 csv에 유니코드를 지원을 안해욤.. ㅜ _-
    그래서 우리는 쉼표가 아닌 tab으로 구분하는 csv로 변경했스빈다.
    ( tab으로 구분하는 녀석은 유니코드 지원을 해욤.. )

  4. super chicken 2007/10/18 06:05  댓글주소  수정/삭제  댓글쓰기

    친구는 너의 위치의 현재 팬이 되었다!