C ++에서 CSV 파일 데이터를 어떻게 읽고 조작 할 수 있습니까? [복제하다

StackOverflow https://stackoverflow.com/questions/415515

  •  03-07-2019
  •  | 
  •  

문제

이 질문은 이미 여기에 답이 있습니다.

꽤 자명 한, 나는 Google을 시험해 보았고, 끔찍한 ExpertsexChange를 많이 얻었습니다. 온라인 튜토리얼이나 예가 가장 좋습니다. 감사합니다.

도움이 되었습니까?

해결책

당신이 실제로하고있는 일이 CSV 파일 자체를 조작하는 것이라면 넬슨의 대답은 의미가 있습니다. 그러나 내 의심은 CSV가 단순히 해결하는 문제의 인공물이라는 것입니다. C ++에서는 아마도 데이터 모델과 같은 것을 가지고 있음을 의미합니다.

struct Customer {
    int id;
    std::string first_name;
    std::string last_name;
    struct {
        std::string street;
        std::string unit;
    } address;
    char state[2];
    int zip;
};

따라서 데이터 모음으로 작업 할 때는 std::vector<Customer> 또는 std::set<Customer>.

이를 염두에두고 CSV 처리를 두 가지 작업으로 생각하십시오.

// if you wanted to go nuts, you could use a forward iterator concept for both of these
class CSVReader {
public:
    CSVReader(const std::string &inputFile);
    bool hasNextLine();
    void readNextLine(std::vector<std::string> &fields);
private:
    /* secrets */
};
class CSVWriter {
public:
    CSVWriter(const std::string &outputFile);
    void writeNextLine(const std::vector<std::string> &fields);
private:
    /* more secrets */
};
void readCustomers(CSVReader &reader, std::vector<Customer> &customers);
void writeCustomers(CSVWriter &writer, const std::vector<Customer> &customers);

파일 자체의 완전한 메모리 표현을 유지하지 않고 한 번에 단일 행을 읽고 씁니다. 몇 가지 명백한 이점이 있습니다.

  1. 귀하의 데이터는 현재 솔루션 (CSV 파일)이 아닌 문제 (고객)에 적합한 양식으로 표시됩니다.
  2. 대량 SQL 가져 오기/내보내기, Excel/OO 스프레드 시트 파일 또는 HTML과 같은 다른 데이터 형식에 대한 어댑터를 사소하게 추가 할 수 있습니다. <table> 표현.
  3. 메모리 발자국은 더 작을 가능성이 높습니다 (상대적 sizeof(Customer) 단일 행의 바이트 수).
  4. CSVReader 그리고 CSVWriter 성능이나 기능 상실없이 메모리 내 모델 (예 : Nelson)의 기초로 재사용 할 수 있습니다. 대화는 사실이 아닙니다.

다른 팁

더 많은 정보가 유용 할 것입니다.

그러나 가장 간단한 형태 :

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>

int main()
{
    std::ifstream  data("plop.csv");

    std::string line;
    while(std::getline(data,line))
    {
        std::stringstream  lineStream(line);
        std::string        cell;
        while(std::getline(lineStream,cell,','))
        {
            // You have a cell!!!!
        }
    }
 }

또한이 질문을 참조하십시오. C ++의 CSV 파서

Boost Tokenizer 라이브러리, 특히 탈출 된 목록 분리기

저는 제 시간에 많은 CSV 파일과 함께 일했습니다. 조언을 추가하고 싶습니다.

1- 소스 (Excel 등)에 따라 쉼표 또는 탭이 필드에 포함될 수 있습니다. 일반적으로 규칙은 "보스턴, MA 02346"에서와 같이 필드가 두 배로 구분되기 때문에 '보호'될 것입니다.

2- 일부 소스는 모든 텍스트 필드를 두 배로 늘리지 않습니다. 다른 출처는 할 것입니다. 다른 사람들은 모든 필드, 심지어 숫자를 구분할 것입니다.

3- 이중 크기가 포함 된 필드는 일반적으로 내장 된 이중 인용문을 두 배로 늘립니다 (그리고 필드 자체는 "George" "Babe" "Ruth"에서와 같이 이중 인용문으로 구분됩니다.

4- 일부 소스는 CR/LFS를 포함합니다 (Excel은이 중 하나입니다!). 때로는 CR 일뿐입니다. 이 필드는 일반적으로 이중 쿼트가 구분되지만이 상황은 다루기가 매우 어렵습니다.

이것은 자신이 일하기에 좋은 운동입니다 :)

도서관을 세 부분으로 나누어야합니다

  • CSV 파일로드
  • 파일을 메모리로 표현하여 수정하고 읽을 수 있습니다.
  • CSV 파일을 디스크에 다시 저장합니다

그래서 당신은 다음을 포함하는 csvdocument 클래스를 작성하고 있습니다.

  • 로드 (const char* 파일);
  • 저장 (const char* 파일);
  • 구체

다음과 같이 도서관을 사용할 수 있습니다.

CSVDocument doc;
doc.Load("file.csv");
CSVDocumentBody* body = doc.GetBody();

CSVDocumentRow* header = body->GetRow(0);
for (int i = 0; i < header->GetFieldCount(); i++)
{
    CSVDocumentField* col = header->GetField(i);
    cout << col->GetText() << "\t";
}

for (int i = 1; i < body->GetRowCount(); i++) // i = 1 so we skip the header
{
    CSVDocumentRow* row = body->GetRow(i);
    for (int p = 0; p < row->GetFieldCount(); p++)
    {
        cout << row->GetField(p)->GetText() << "\t";
    }
    cout << "\n";
}

body->GetRecord(10)->SetText("hello world");

CSVDocumentRow* lastRow = body->AddRow();
lastRow->AddField()->SetText("Hey there");
lastRow->AddField()->SetText("Hey there column 2");

doc->Save("file.csv");

다음 인터페이스를 제공합니다.

class CSVDocument
{
public:
    void Load(const char* file);
    void Save(const char* file);

    CSVDocumentBody* GetBody();
};

class CSVDocumentBody
{
public:
    int GetRowCount();
    CSVDocumentRow* GetRow(int index);
    CSVDocumentRow* AddRow();
};

class CSVDocumentRow
{
public:
    int GetFieldCount();
    CSVDocumentField* GetField(int index);
    CSVDocumentField* AddField(int index);
};

class CSVDocumentField
{
public:
    const char* GetText();
    void GetText(const char* text);
};

이제 여기에서 공란을 채워야합니다 :)

내가 이것을 말할 때 나를 믿으십시오 - 라이브러리를 만드는 방법, 특히 데이터 저축을 다루는 방법을 배우는 방법을 배우는 데 시간을 투자하면 그러한 라이브러리의 존재에 대한 의존도를 제거 할뿐만 아니라 당신을 모두로 만들 것입니다. 더 나은 프로그래머 주변.

:)

편집하다

나는 당신이 현악 조작과 구문 분석에 대해 이미 얼마나 알고 있는지 모르겠습니다. 그래서 당신이 붙어 있다면 나는 기꺼이 도와 드릴 것입니다.

다음은 사용할 수있는 몇 가지 코드입니다. CSV의 데이터는 행 배열 내에 저장됩니다. 각 행은 문자열 배열입니다. 도움이 되었기를 바랍니다.

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
typedef std::string String;
typedef std::vector<String> CSVRow;
typedef CSVRow::const_iterator CSVRowCI;
typedef std::vector<CSVRow> CSVDatabase;
typedef CSVDatabase::const_iterator CSVDatabaseCI;
void readCSV(std::istream &input, CSVDatabase &db);
void display(const CSVRow&);
void display(const CSVDatabase&);
int main(){
  std::fstream file("file.csv", std::ios::in);
  if(!file.is_open()){
    std::cout << "File not found!\n";
    return 1;
  }
  CSVDatabase db;
  readCSV(file, db);
  display(db);
}
void readCSV(std::istream &input, CSVDatabase &db){
  String csvLine;
  // read every line from the stream
  while( std::getline(input, csvLine) ){
    std::istringstream csvStream(csvLine);
    CSVRow csvRow;
    String csvCol;
    // read every element from the line that is seperated by commas
    // and put it into the vector or strings
    while( std::getline(csvStream, csvCol, ',') )
      csvRow.push_back(csvCol);
    db.push_back(csvRow);
  }
}
void display(const CSVRow& row){
  if(!row.size())
    return;
  CSVRowCI i=row.begin();
  std::cout<<*(i++);
  for(;i != row.end();++i)
    std::cout<<','<<*i;
}
void display(const CSVDatabase& db){
  if(!db.size())
    return;
  CSVDatabaseCI i=db.begin();
  for(; i != db.end(); ++i){
    display(*i);
    std::cout<<std::endl;
  }
}

부스트 토 케이저를 사용하여 레코드를 구문 분석합니다, 자세한 내용은 여기를 참조하십시오.

ifstream in(data.c_str());
if (!in.is_open()) return 1;

typedef tokenizer< escaped_list_separator<char> > Tokenizer;

vector< string > vec;
string line;

while (getline(in,line))
{
    Tokenizer tok(line);
    vec.assign(tok.begin(),tok.end());

    /// do something with the record
    if (vec.size() < 3) continue;

    copy(vec.begin(), vec.end(),
         ostream_iterator<string>(cout, "|"));

    cout << "\n----------------------" << endl;
}

보다 '프로그래밍 연습'(TPOP)의 Kernighan & Pike. C 및 C ++ 모두에서 CSV 파일을 구문 분석하는 예가 포함되어 있습니다. 그러나 코드를 사용하지 않더라도 책을 읽을 가치가 있습니다.

(이전 URL : http://cm.bell-labs.com/cm/cs/tpop/)

나는이 흥미로운 접근법을 발견했다.

CSV에서 C 구조 유틸리티

인용구 : CSVTOC은 CSV 또는 쉼표로 분리 된 값 파일을 입력으로 가져 와서 C 구조로 덤프하는 프로그램입니다.

당연히 CSV 파일을 변경할 수는 없지만 데이터에 대한 메모리 내 읽기 전용 액세스가 필요한 경우 작동 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top