¿Cómo puedo leer y manipular datos de archivos CSV en C++?[duplicar]

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

  •  03-07-2019
  •  | 
  •  

Pregunta

Esta pregunta ya tiene respuesta aquí:

Se explica por sí mismo, probé Google y obtuve muchos del temido intercambio de expertos, también busqué aquí sin éxito.Lo mejor sería un tutorial o un ejemplo en línea.Gracias chicos.

¿Fue útil?

Solución

Si lo que realmente estás haciendo es manipular un archivo CSV, la respuesta de Nelson tiene sentido.Sin embargo, mi sospecha es que el CSV es simplemente un artefacto del problema que estás resolviendo.En C++, eso probablemente significa que tienes algo como esto como modelo de datos:

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

Por lo tanto, cuando se trabaja con una colección de datos, tiene sentido tener std::vector<Customer> o std::set<Customer>.

Con eso en mente, piense en el manejo de CSV como dos operaciones:

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

Lea y escriba una sola fila a la vez, en lugar de mantener una representación completa en memoria del archivo en sí.Hay algunos beneficios obvios:

  1. Sus datos se representan en una forma que tenga sentido para su problema (clientes), en lugar de la solución actual (archivos CSV).
  2. Puede agregar trivialmente adaptadores para otros formatos de datos, como importación/exportación masiva de SQL, archivos de hojas de cálculo Excel/OO o incluso HTML. <table> representación.
  3. Es probable que su huella de memoria sea menor (depende de la relativa sizeof(Customer) vs.el número de bytes en una sola fila).
  4. CSVReader y CSVWriter se puede reutilizar como base para un modelo en memoria (como el de Nelson) sin pérdida de rendimiento o funcionalidad.Lo contrario no es cierto.

Otros consejos

Más información sería útil.

Pero la forma más simple:

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

Consulte también esta pregunta: Analizador CSV en C ++

Puede probar la biblioteca Boost Tokenizer, en particular Escaped Separador de listas

He trabajado con muchos archivos CSV en mi tiempo. Me gustaría agregar el consejo:

1 - Dependiendo de la fuente (Excel, etc.), se pueden incrustar comas o pestañas en un campo. Por lo general, la regla es que estarán 'protegidos' porque el campo estará delimitado por comillas dobles, como en & Quot; Boston, MA 02346 & Quot ;.

2 - Algunas fuentes no delimitan con comillas dobles todos los campos de texto. Otras fuentes lo harán. Otros delimitarán todos los campos, incluso los numéricos.

3 - Los campos que contienen comillas dobles suelen duplicar las comillas dobles incrustadas (y el campo en sí mismo delimitado con comillas dobles, como en " George " " Babe " " Ruth " ;.

4 - Algunas fuentes incrustarán CR / LF (¡Excel es uno de estos!). A veces será solo un CR. El campo generalmente estará delimitado por comillas dobles, pero esta situación es muy difícil de manejar.

Este es un buen ejercicio para trabajar :)

Debe dividir su biblioteca en tres partes

  • Cargando el archivo CSV
  • Representar el archivo en la memoria para que pueda modificarlo y leerlo
  • Guardar el archivo CSV nuevamente en el disco

Entonces, está buscando escribir una clase CSVDocument que contenga:

  • Cargar (archivo const char *);
  • Guardar (archivo const char *);
  • GetBody

Para que pueda usar su biblioteca de esta manera:

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

Lo que nos da las siguientes 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);
};

Ahora solo tiene que completar los espacios en blanco desde aquí :)

Créame cuando digo esto: invertir su tiempo en aprender a crear bibliotecas, especialmente aquellas relacionadas con la carga, la manipulación y el almacenamiento de datos, no solo eliminará su dependencia de la existencia de dichas bibliotecas, sino que también lo hará un mejor programador completo.

:)

EDIT

No sé cuánto ya sabes sobre manipulación y análisis de cadenas; así que si te quedas atascado, estaré encantado de ayudarte.

Aquí hay un código que puede usar. Los datos del csv se almacenan dentro de una matriz de filas. Cada fila es una matriz de cadenas. Espero que esto ayude.

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

Uso de tokenizer boost para analizar registros , vea aquí para más detalles .

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

Mire ' La práctica de la programación ' (TPOP) por Kernighan & amp; Lucio. Incluye un ejemplo de análisis de archivos CSV en C y C ++. Pero valdría la pena leer el libro incluso si no usa el código.

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

Encontré este enfoque interesante:

utilidad de estructura CSV a C

Cita: CSVtoC es un programa que toma un archivo de valores separados por comas o CSV como entrada y lo descarga como una estructura C.

Naturalmente, no puede realizar cambios en el archivo CSV, pero si solo necesita acceso de memoria de solo lectura a los datos, podría funcionar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top