Como posso ler e manipular dados de arquivos CSV em C ++? [duplicado]

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

  •  03-07-2019
  •  | 
  •  

Pergunta

Esta questão já tem uma resposta aqui:

Pretty auto-explicativo, eu tentei google e tem um monte de expertsexchange temida, eu procurei aqui também sem sucesso. Um tutorial online ou exemplo seria melhor. Obrigado rapazes.

Foi útil?

Solução

Se o que você está fazendo realmente está manipulando um arquivo CSV em si, a resposta de Nelson faz sentido. No entanto, a minha suspeita é que o CSV é simplesmente um artefato do problema que você está resolvendo. Em C ++, isso provavelmente significa que você tem algo assim como seu modelo de dados:

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;
};

Assim, quando você está trabalhando com uma coleção de dados, faz sentido ter std::vector<Customer> ou std::set<Customer>.

Com isso em mente, pense em seu CSV manipulação como duas operações:

// 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);

Ler e escrever uma única linha de cada vez, em vez de manter uma representação completa na memória do próprio arquivo. Existem alguns benefícios óbvios:

  1. Seus dados são representados em uma forma que faça sentido para o seu problema (clientes), mais do que a solução atual (arquivos CSV).
  2. Você pode trivialmente adicionar adaptadores para outros formatos de dados, tais como a importação SQL granel / exportação, Excel / planilhas OO, ou até mesmo uma prestação <table> HTML.
  3. O seu consumo de memória é provável que seja menor (depende sizeof(Customer) relativa vs. o número de bytes em uma única linha).
  4. CSVReader e CSVWriter podem ser reutilizados como a base para um modelo in-memory (como Nelson) sem perda de desempenho ou funcionalidade. O inverso não é verdadeiro.

Outras dicas

Mais informação pode ser útil.

Mas a forma mais simples:

#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!!!!
        }
    }
 }

Veja também esta pergunta: CSV analisador em C ++

Você pode tentar o impulso biblioteca Tokenizer, em particular a Escapou Separador de lista

Eu trabalhei com um monte de arquivos CSV no meu tempo. Eu gostaria de adicionar o conselho:

1 - Dependendo da fonte (Excel, etc.), vírgulas ou abas pode ser incorporado em um campo. Normalmente, a regra é que eles vão ser 'protegidos' porque o campo será double-quote delimitado, como em "Boston, MA 02346".

2 - Algumas fontes não vai double-quote delimitar todos os campos de texto. Outras fontes vão. Outros vão delimitar todos os campos, mesmo numerics.

3 -. Campos contendo aspas geralmente obter as aspas duplas incorporados dobrou (e o próprio campo delimitado com aspas duplas, como em "George '' Babe" "Ruth"

4 - Algumas fontes irá incorporar CR / LFs (Excel é uma delas!). Às vezes ele vai ser apenas um CR. O campo será normalmente aspas delimitada, mas esta situação é muito difícil de lidar.

Este é um bom exercício para si mesmo para os trabalhos sobre:)

Você deve quebrar a sua biblioteca em três partes

  • Carregando o arquivo CSV
  • Representando o arquivo na memória para que você possa modificá-lo e lê-lo
  • Salvar o arquivo CSV de volta para o disco

Então, você está olhando para escrever uma classe CSVDocument que contém:

  • Load (char const * arquivo);
  • Save (char const * arquivo);
  • GetBody

Para que você possa usar sua biblioteca como esta:

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");

O que nos dá as seguintes interfaces:

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);
};

Agora você só tem que preencher os espaços em branco a partir daqui:)

Acredite em mim quando digo isso - investir seu tempo em aprender como fazer bibliotecas, especialmente aqueles que lidam com a carga, manipulação e armazenamento de dados, não só irá remover a sua dependência em relação à existência de tais bibliotecas, mas também vai fazer você um conjunto de cerca programador melhor.

:)

Editar

Eu não sei o quanto você já sabe sobre a manipulação de cadeia e análise; por isso, se você ficar preso Eu ficaria feliz em ajudar.

Aqui está um código que você pode usar. Os dados do CSV é armazenado dentro de uma matriz de linhas. Cada linha é uma matriz de cadeias. Espero que isso ajude.

#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;
  }
}

Usando boost tokenizer com registros de análise , veja aqui para mais detalhes .

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;
}

Look at ' The Practice of Programming ' (TPOP) por Kernighan e Pike. Ele inclui um exemplo de análise de arquivos CSV em C e C ++. Mas seria pena ler o livro, mesmo se você não usar o código.

(URL anterior: http://cm.bell-labs.com/ cm / cs / TPOP / )

Eu encontrei esta abordagem interessante:

CSV a utilidade estrutura C

Citação: CSVtoC é um programa que usa um arquivo CSV ou valores separados por vírgulas como entrada e devolve-lo como uma estrutura C.

Naturalmente, você não pode fazer alterações para o arquivo CSV, mas se você só precisa na memória somente leitura o acesso aos dados, que poderia funcionar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top