1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
#ifndef CSVPARSER_HPP
#define CSVPARSER_HPP
#include <iterator>
#include <functional>
/// @brief Représente un parseur de CSV.
/// La classe accepte tout format itérable. En conséquence il est possible de parser un std::vector<int>
/// en définissant par exemple 0 comme délimiteur de cellule, 1 comme délimiteur de ligne et 2 comme
/// délimiteur d'échappement
template <class T>
class CsvParser
{
private:
typename T::value_type m_cell_delim;
typename T::value_type m_line_delim;
typename T::value_type m_escp_delim;
typename T::const_iterator m_position;
typename T::const_iterator m_end;
unsigned long m_row;
unsigned long m_col;
unsigned long m_cell;
std::function<bool ()> m_parse;
/// @brief État "normal", on accepte tout caractère qui n'est pas un délimiteur.
/// @returns true pour indiquer qu'il faut accepter l'élément courant, sinon false.
/// Si le délimiteur d'échappement est rencontré, on change d'état, la fonction de parse active
/// devient parse_escaped.
bool parse_default()
{
if (m_position != m_end)
{
typename T::value_type i = *m_position;
if (i == m_cell_delim)
{
m_position++;
m_col++;
m_cell++;
return false;
}
if (i == m_line_delim)
{
m_position++;
m_col = 0;
m_cell++;
m_row++;
return false;
}
if (i == m_escp_delim)
{
m_position++;
m_parse = std::bind(&CsvParser<T>::parse_escaped, this);
return m_parse();
}
return true;
}
return false;
}
/// @brief État "echapé", on accepte tout caractère qui n'est pas un délimiteur d'échappement.
/// @returns true pour indiquer qu'il faut accepter l'élément courant, sinon false.
/// Si le délimiteur d'échappement est rencontré, on vérifie s'il est doublé, si oui on accepte le caractère
/// sinon on change d'état, la fonction de parse active devient parse_default.
bool parse_escaped()
{
if(m_position != m_end)
{
typename T::value_type i = *m_position;
if (i == m_escp_delim)
{
m_position++; // On passe à l'élément suivant
if (m_position != m_end && *m_position == m_escp_delim) return true;
m_parse = std::bind(&CsvParser<T>::parse_default, this);
return m_parse();
}
return true;
}
return false;
}
public:
/// @brief Construit un parseur CSV.
/// @param[in] cell_delim Le délimiteur de cellule, généralement une tabulation, une virgule ou un point-virgule quand la donnée à lire est du texte.
/// @param[in] line_delim Le délimiteur de ligne, généralement un retour chariot quand la donnée à lire est du texte.
/// @param[in] espc_delim Le délimiteur d'échappement, générallement un guillemet quand la donnée à lire est du texte.
CsvParser(const T& value, typename T::value_type cell_delim, typename T::value_type line_delim, typename T::value_type escp_delim)
: m_position(std::begin(value))
, m_end(std::end(value))
, m_row(0)
, m_col(0)
, m_cell(0)
, m_cell_delim(cell_delim)
, m_line_delim(line_delim)
, m_escp_delim(escp_delim)
, m_parse(std::bind(&CsvParser<T>::parse_default, this))
{
}
/// @brief Obtient la ligne en cours de lecture.
unsigned long currentRow() const { return m_row; }
/// @brief Obtient la colonne en cours de lecture.
unsigned long currentCol() const { return m_col; }
/// @brief Obtient la cellule en cours de lecture.
unsigned long currentCell() const { return m_cell; }
/// @brief Permet de vérifier si le parser à encore des données à lire.
explicit operator bool() const { return m_position != m_end; }
/// @brief Obtient la valeur suivante.
T ReadNext()
{
T value;
while(m_parse())
{
value.push_back(*m_position);
m_position++;
}
return value;
}
/// @brief Permet une syntaxe du style p >> c1 >> c2;
CsvParser<T>& operator>>(T& value)
{
value = ReadNext();
return *this;
}
};
#endif // CSVPARSER_HPP |
Partager