Assez explicite, j'ai essayé google et j'ai obtenu beaucoup des échanges d'experts redoutés, j'ai également cherché ici en vain. Un tutoriel ou un exemple en ligne serait préférable. Merci les gars.
Si ce que vous faites réellement est de manipuler un fichier CSV lui-même, la réponse de Nelson est logique. Cependant, je soupçonne que le CSV est simplement un artefact du problème que vous résolvez. En C++, cela signifie probablement que vous avez quelque chose comme ça comme modèle de données:
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;
};
Ainsi, lorsque vous travaillez avec une collection de données, il est logique d'avoir std::vector<Customer>
Ou std::set<Customer>
.
Dans cet esprit, pensez à votre traitement CSV comme deux opérations:
// 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);
Lisez et écrivez une seule ligne à la fois, plutôt que de conserver une représentation complète en mémoire du fichier lui-même. Il y a quelques avantages évidents:
<table>
.sizeof(Customer)
par rapport au nombre d'octets sur une seule ligne).CSVReader
et CSVWriter
peuvent être réutilisés comme base pour un modèle en mémoire (comme celui de Nelson) sans perte de performances ou de fonctionnalité. L'inverse est pas vrai.Plus d'informations seraient utiles.
Mais la forme la plus 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!!!!
}
}
}
Voir aussi cette question: analyseur CSV en C++
Vous pouvez essayer la bibliothèque Boost Tokenizer, en particulier le Escaped List Separator
J'ai travaillé avec beaucoup de fichiers CSV de mon temps. Je voudrais ajouter le conseil:
1 - Selon la source (Excel, etc.), des virgules ou des tabulations peuvent être intégrées dans un champ. Habituellement, la règle est qu'ils seront "protégés" car le champ sera délimité par des guillemets doubles, comme dans "Boston, MA 02346".
2 - Certaines sources ne délimitent pas tous les champs de texte. D'autres sources le feront. D'autres délimiteront tous les champs, même numériques.
3 - Les champs contenant des guillemets doubles sont généralement doublés (et le champ lui-même délimité par des guillemets doubles, comme dans "George" "Babe" "Ruth").
4 - Certaines sources intégreront des CR/LF (Excel en fait partie!). Parfois, ce sera juste un CR. Le champ sera généralement délimité par des guillemets doubles, mais cette situation est très difficile à gérer.
C'est un bon exercice pour vous de travailler :)
Vous devez diviser votre bibliothèque en trois parties
Vous envisagez donc d'écrire une classe CSVDocument qui contient:
Afin que vous puissiez utiliser votre bibliothèque comme ceci:
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");
Ce qui nous donne les interfaces suivantes:
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);
};
Il ne vous reste plus qu'à remplir les blancs d'ici :)
Croyez-moi quand je dis ceci - investir votre temps dans l'apprentissage de la création de bibliothèques, en particulier celles traitant du chargement, de la manipulation et de la sauvegarde des données, vous permettra non seulement de supprimer votre dépendance à l'existence de telles bibliothèques, mais vous fera également un tout- autour d'un meilleur programmeur.
:)
MODIFIER
Je ne sais pas ce que vous savez déjà sur la manipulation et l'analyse de chaînes; donc si vous êtes coincé, je serais heureux de vous aider.
Voici un code que vous pouvez utiliser. Les données du csv sont stockées dans un tableau de lignes. Chaque ligne est un tableau de chaînes. J'espère que cela t'aides.
#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;
}
}
Utilisation du tokenizer boost pour analyser les enregistrements , voir ici pour plus de détails .
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;
}
Regardez ' The Practice of Programming ' (TPOP) par Kernighan & Pike. Il comprend un exemple d'analyse des fichiers CSV en C et C++. Mais cela vaut la peine de lire le livre même si vous n'utilisez pas le code.
(URL précédente: http://cm.bell-labs.com/cm/cs/tpop/ )
J'ai trouvé cette approche intéressante:
tilitaire de structure CSV en C
Quote: CSVtoC est un programme qui prend un fichier CSV ou des valeurs séparées par des virgules en entrée et le sauvegarde sous forme de structure C.
Naturellement, vous ne pouvez pas apporter de modifications au fichier CSV, mais si vous avez juste besoin d'un accès en lecture seule en mémoire aux données, cela pourrait fonctionner.