Come posso leggere e manipolare i dati dei file CSV in C ++? [duplicare]
Domanda
Questa domanda ha già una risposta qui:
Abbastanza autoesplicativo, ho provato google e ho avuto un sacco di temuti scambi di esperti, ho cercato anche qui senza alcun risultato. Un tutorial o esempio online sarebbe il migliore. Grazie ragazzi.
Soluzione
Se quello che stai veramente facendo è manipolare un file CSV stesso, la risposta di Nelson ha senso. Tuttavia, il mio sospetto è che il CSV sia semplicemente un artefatto del problema che stai risolvendo. In C ++, ciò probabilmente significa che hai qualcosa del genere come modello di dati:
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;
};
Pertanto, quando si lavora con una raccolta di dati, ha senso avere std::vector<Customer>
o std::set<Customer>
.
Con questo in mente, pensa alla tua gestione CSV come a due operazioni:
// 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);
Leggi e scrivi una singola riga alla volta, invece di mantenere una rappresentazione in memoria completa del file stesso. Ci sono alcuni ovvi benefici:
- I tuoi dati sono rappresentati in una forma che ha senso per il tuo problema (clienti), piuttosto che la soluzione corrente (file CSV).
- Puoi aggiungere banalmente adattatori per altri formati di dati, come importazione / esportazione di massa SQL, file di fogli di calcolo Excel / OO o persino un rendering HTML
<table>
. - È probabile che il tuo footprint di memoria sia più piccolo (dipende dal
sizeof(Customer)
relativo rispetto al numero di byte in una singola riga). -
CSVReader
eCSVWriter
possono essere riutilizzati come base per un modello in memoria (come quello di Nelson) senza perdita di prestazioni o funzionalità. Il contrario non è vero.
Altri suggerimenti
Ulteriori informazioni sarebbero utili.
Ma la forma più semplice:
#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!!!!
}
}
}
Vedi anche questa domanda: parser CSV in C ++
Puoi provare la libreria Boost Tokenizer, in particolare Escaped Separatore elenco
Ho lavorato con molti file CSV ai miei tempi. Vorrei aggiungere il consiglio:
1 - A seconda dell'origine (Excel, ecc.), le virgole o le schede possono essere incorporate in un campo. Di solito, la regola è che saranno "protetti" perché il campo sarà delimitato da virgolette doppie, come in & Quot; Boston, MA 02346 & Quot ;.
2 - Alcune fonti non racchiuderanno virgolette doppie di tutti i campi di testo. Altre fonti lo faranno. Altri delimiteranno tutti i campi, anche i numeri.
3 - I campi contenenti virgolette doppie di solito raddoppiano le doppie virgolette incorporate (e il campo stesso delimitato da virgolette doppie, come in " George " " Babe " " Ruth " ;.
4 - Alcune fonti incorporeranno CR / LF (Excel è uno di questi!). A volte sarà solo un CR. Il campo sarà solitamente delimitato da virgolette doppie, ma questa situazione è molto difficile da gestire.
Questo è un buon esercizio su cui lavorare :)
Dovresti suddividere la tua libreria in tre parti
- Caricamento del file CSV
- Rappresenta il file in memoria in modo da poterlo modificare e leggerlo
- Salvataggio del file CSV su disco
Quindi stai cercando di scrivere una classe CSVDocument che contiene:
- Carica (const char * file);
- Salva (const char * file);
- getBody
Per poter utilizzare la tua libreria in questo modo:
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");
Che ci dà le seguenti interfacce:
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);
};
Ora devi solo riempire gli spazi vuoti da qui :)
Credimi quando dico questo: investire il tuo tempo nell'imparare a creare biblioteche, specialmente quelle che si occupano di caricamento, manipolazione e salvataggio dei dati, non solo rimuoverà la tua dipendenza dall'esistenza di tali biblioteche, ma ti farà anche un programmatore migliore a tutto tondo.
:)
Modifica
Non so quanto tu sappia già sulla manipolazione e l'analisi delle stringhe; quindi se rimani bloccato sarei felice di aiutarti.
Ecco un codice che puoi usare. I dati dal CSV sono memorizzati in una matrice di righe. Ogni riga è una matrice di stringhe. Spero che questo aiuti.
#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;
}
}
Utilizzo del tokenizer boost per analizzare i record , vedi qui per maggiori dettagli .
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;
}
Guarda ' The Practice of Programming ' (TPOP) di Kernighan & amp; Luccio. Include un esempio di analisi dei file CSV in C e C ++. Ma varrebbe la pena leggere il libro anche se non si utilizza il codice.
(URL precedente: http://cm.bell-labs.com/ cm / cs / tpop / )
Ho trovato questo approccio interessante:
Citazione: CSVtoC è un programma che accetta come input un file di valori CSV o separato da virgole e lo scarica come struttura C.
Naturalmente, non è possibile apportare modifiche al file CSV, ma se si necessita solo dell'accesso in sola lettura ai dati, potrebbe funzionare.