Si vous ne vous souciez pas de l'échappement des virgules et des nouvelles lignes,
ET vous ne pouvez pas intégrer la virgule et la nouvelle ligne dans les guillemets (Si vous ne pouvez pas les échapper alors...)
alors ce n'est qu'environ trois lignes de code (OK 14 ->Mais ce n'est que 15 pour lire le fichier entier).
std::vector<std::string> getNextLineAndSplitIntoTokens(std::istream& str)
{
std::vector<std::string> result;
std::string line;
std::getline(str,line);
std::stringstream lineStream(line);
std::string cell;
while(std::getline(lineStream,cell, ','))
{
result.push_back(cell);
}
// This checks for a trailing comma with no data after it.
if (!lineStream && cell.empty())
{
// If there was a trailing comma then add an empty element.
result.push_back("");
}
return result;
}
Je créerais simplement une classe représentant une ligne.
Ensuite, le flux dans cet objet :
#include <iterator>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
class CSVRow
{
public:
std::string_view operator[](std::size_t index) const
{
return std::string_view(&m_line[m_data[index] + 1], m_data[index + 1] - (m_data[index] + 1));
}
std::size_t size() const
{
return m_data.size() - 1;
}
void readNextRow(std::istream& str)
{
std::getline(str, m_line);
m_data.clear();
m_data.emplace_back(-1);
std::string::size_type pos = 0;
while((pos = m_line.find(',', pos)) != std::string::npos)
{
m_data.emplace_back(pos);
++pos;
}
// This checks for a trailing comma with no data after it.
pos = m_line.size();
m_data.emplace_back(pos);
}
private:
std::string m_line;
std::vector<int> m_data;
};
std::istream& operator>>(std::istream& str, CSVRow& data)
{
data.readNextRow(str);
return str;
}
int main()
{
std::ifstream file("plop.csv");
CSVRow row;
while(file >> row)
{
std::cout << "4th Element(" << row[3] << ")\n";
}
}
Mais avec un peu de travail, nous pourrions techniquement créer un itérateur :
class CSVIterator
{
public:
typedef std::input_iterator_tag iterator_category;
typedef CSVRow value_type;
typedef std::size_t difference_type;
typedef CSVRow* pointer;
typedef CSVRow& reference;
CSVIterator(std::istream& str) :m_str(str.good()?&str:NULL) { ++(*this); }
CSVIterator() :m_str(NULL) {}
// Pre Increment
CSVIterator& operator++() {if (m_str) { if (!((*m_str) >> m_row)){m_str = NULL;}}return *this;}
// Post increment
CSVIterator operator++(int) {CSVIterator tmp(*this);++(*this);return tmp;}
CSVRow const& operator*() const {return m_row;}
CSVRow const* operator->() const {return &m_row;}
bool operator==(CSVIterator const& rhs) {return ((this == &rhs) || ((this->m_str == NULL) && (rhs.m_str == NULL)));}
bool operator!=(CSVIterator const& rhs) {return !((*this) == rhs);}
private:
std::istream* m_str;
CSVRow m_row;
};
int main()
{
std::ifstream file("plop.csv");
for(CSVIterator loop(file); loop != CSVIterator(); ++loop)
{
std::cout << "4th Element(" << (*loop)[3] << ")\n";
}
}
Maintenant que nous sommes en 2020, ajoutons un objet CSVRange :
class CSVRange
{
std::istream& stream;
public:
CSVRange(std::istream& str)
: stream(str)
{}
CSVIterator begin() const {return CSVIterator{stream};}
CSVIterator end() const {return CSVIterator{};}
};
int main()
{
std::ifstream file("plop.csv");
for(auto& row: CSVRange(file))
{
std::cout << "4th Element(" << row[3] << ")\n";
}
}
11 votes
J'ai regardé
boost::spirit
pour l'analyse syntaxique. Il est plus destiné à l'analyse grammaticale qu'à l'analyse d'un simple format de fichier. Quelqu'un de mon équipe a essayé de l'utiliser pour analyser le XML et c'était une douleur à déboguer. Ne vous approchez pas deboost::spirit
si possible.51 votes
Désolé Chrish, mais c'est un mauvais conseil. Spirit n'est pas toujours une solution appropriée mais je l'ai utilisé - et continue de l'utiliser - avec succès dans un certain nombre de projets. Comparé à des outils similaires (Antlr, Lex/yacc etc.) il a des avantages significatifs. Maintenant, pour analyser un CSV, c'est probablement trop...
4 votes
@MattyT IMHO
spirit
est assez difficile à utiliser pour une bibliothèque de combinateurs d'analyseurs. Ayant eu une certaine expérience (très agréable) avec Haskells(atto)parsec
bibliothèques Je m'attendais à ce que (l'esprit) fonctionne aussi bien, mais j'ai abandonné après m'être battu avec des erreurs de compilation de 600 lignes.1 votes
C CSV Parser : sourceforge.net/projets/cccsvparser C CSV Writer : sourceforge.net/projets/cccsvwriter
0 votes
Pourquoi ne pas vouloir échapper aux virgules et aux nouvelles lignes ! Toutes les recherches renvoient à cette question et je n'ai pas pu trouver une seule réponse qui prenne en compte l'échappement ! :|