it-swarm.com.de

Wie kann ich CSV-Dateidaten in C ++ lesen und bearbeiten?

Ziemlich selbsterklärend, ich habe Google ausprobiert und viel von dem gefürchteten Experten-Austausch mitbekommen, ich habe auch hier erfolglos gesucht. Ein Online-Tutorial oder Beispiel wäre am besten. Danke Leute.

51
zkwentz

Wenn Sie wirklich eine CSV-Datei selbst manipulieren, ist die Antwort von Nelson sinnvoll. Mein Verdacht ist jedoch, dass die CSV lediglich ein Artefakt des Problems ist, das Sie lösen. In C++ bedeutet das wahrscheinlich, dass Sie so etwas als Datenmodell haben:

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

Wenn Sie also mit einer Sammlung von Daten arbeiten, ist es sinnvoll, std::vector<Customer> Oder std::set<Customer> Zu haben.

Stellen Sie sich Ihre CSV-Verarbeitung in diesem Sinne als zwei Operationen vor:

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

Lesen und schreiben Sie jeweils eine einzelne Zeile, anstatt eine vollständige speicherinterne Darstellung der Datei selbst zu erhalten. Es gibt ein paar offensichtliche Vorteile:

  1. Ihre Daten werden in einer Form dargestellt, die für Ihr Problem (Kunden) und nicht für die aktuelle Lösung (CSV-Dateien) sinnvoll ist.
  2. Sie können problemlos Adapter für andere Datenformate hinzufügen, z. B. SQL-Massenimport/-Export, Excel/OO-Arbeitsblattdateien oder sogar ein HTML-Rendering <table>.
  3. Ihr Speicherbedarf ist wahrscheinlich geringer (hängt von der relativen Anzahl von sizeof(Customer) im Vergleich zur Anzahl von Bytes in einer einzelnen Zeile ab).
  4. CSVReader und CSVWriter können als Grundlage für ein In-Memory-Modell (wie z. B. Nelson) ohne Leistungs- oder Funktionseinbußen wiederverwendet werden. Das Gegenteil ist nicht wahr.
9
Tom

Weitere Informationen wären hilfreich.

Aber die einfachste Form:

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

Siehe auch diese Frage: CSV-Parser in C++

56
Martin York

Sie können die Boost Tokenizer-Bibliothek ausprobieren, insbesondere das Escaped List Separator

21

Ich habe in meiner Zeit mit vielen CSV-Dateien gearbeitet. Ich möchte den Rat hinzufügen:

1 - Abhängig von der Quelle (Excel usw.) können Kommas oder Tabulatoren in ein Feld eingebettet sein. Normalerweise ist die Regel, dass sie 'geschützt' sind, da das Feld durch doppelte Anführungszeichen getrennt ist, wie in "Boston, MA 02346".

2 - Einige Quellen werden nicht alle Textfelder in doppelte Anführungszeichen setzen. Andere Quellen werden. Andere begrenzen alle Felder, auch numerische Felder.

3 - Felder, die doppelte Anführungszeichen enthalten, erhalten normalerweise doppelte Anführungszeichen (und das Feld selbst wird durch doppelte Anführungszeichen begrenzt, wie in "George" "Babe" "Ruth".

4 - Einige Quellen werden CR/LFs einbetten (Excel ist eine davon!). Manchmal ist es nur eine CR. Das Feld wird normalerweise durch doppelte Anführungszeichen abgegrenzt, aber diese Situation ist sehr schwierig zu handhaben.

8
Marc Bernier

Dies ist eine gute Übung, an der du arbeiten kannst :)

Sie sollten Ihre Bibliothek in drei Teile aufteilen

  • Laden der CSV-Datei
  • Darstellen der Datei im Speicher, damit Sie sie ändern und lesen können
  • Speichern der CSV-Datei zurück auf die Festplatte

Sie möchten also eine CSVDocument-Klasse schreiben, die Folgendes enthält:

  • Laden (const char * file);
  • Speichern (const char * Datei);
  • GetBody

Damit Sie Ihre Bibliothek wie folgt nutzen können:

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

Welches gibt uns die folgenden Schnittstellen:

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

Jetzt musst du nur noch die Lücken ausfüllen :)

Glauben Sie mir, wenn ich das sage - wenn Sie Ihre Zeit in das Erlernen der Erstellung von Bibliotheken investieren, insbesondere in das Laden, Bearbeiten und Speichern von Daten, wird dies nicht nur Ihre Abhängigkeit von der Existenz solcher Bibliotheken beseitigen, sondern Sie auch zu einem Alleskönner machen. um bessere Programmierer.

:)

EDIT

Ich weiß nicht, wie viel Sie bereits über String-Manipulation und -Parsing wissen. Wenn Sie nicht weiterkommen, helfe ich Ihnen gerne weiter.

7
user19302

Hier ist ein Code, den Sie verwenden können. Die Daten aus der CSV werden in einem Array von Zeilen gespeichert. Jede Zeile ist ein Array von Zeichenfolgen. Hoffe das hilft.

#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;
  }
}
6
Ashish Jain

Verwenden von Boost-Tokenizer zum Parsen von Datensätzen , weitere Informationen finden Sie hier .

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

Schauen Sie sich ' Die Praxis des Programmierens ' (TPOP) von Kernighan & Pike an. Es enthält ein Beispiel für das Parsen von CSV-Dateien in C und C++. Aber es lohnt sich, das Buch zu lesen, auch wenn Sie den Code nicht verwenden.

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

2

Ich fand diesen interessanten Ansatz:

CSV to C-Strukturdienstprogramm

Zitat: CSVtoC ist ein Programm, das eine CSV- oder kommagetrennte Wertedatei als Eingabe verwendet und als C-Struktur ausgibt.

Natürlich können Sie keine Änderungen an der CSV-Datei vornehmen. Wenn Sie jedoch nur schreibgeschützten Speicherzugriff auf die Daten benötigen, funktioniert dies möglicherweise.

0
Kevin P.