Как читать и анализировать CSV-файлы на C ++?

Мне нужно загрузить и использовать CSV-файлы на C ++. На данный момент это действительно может быть просто разделяемый запятой парсер (т. Е. Не беспокойтесь о побегах новых строк и запятых). Основной потребностью является поэтапный парсер, который будет возвращать вектор для следующей строки каждый раз при вызове метода.

Я нашел эту статью, которая выглядит довольно многообещающе: http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp

Я никогда не использовал Дух Боуста, но я готов попробовать его. Но только если нет более простого решения, я упускаю из виду.

Если вам не нужны экраны запятой и новой строки,
И вы не можете вставлять запятую и новую строчку в кавычки (Если вы не можете убежать тогда …)
то это всего лишь три строки кода (ОК 14 -> Но его всего 15, чтобы прочитать весь файл).

 std::vector getNextLineAndSplitIntoTokens(std::istream& str) { std::vector 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; } 

Я бы просто создал class, представляющий строку.
Затем втекайте в этот объект:

 #include  #include  #include  #include  #include  #include  class CSVRow { public: std::string const& operator[](std::size_t index) const { return m_data[index]; } std::size_t size() const { return m_data.size(); } void readNextRow(std::istream& str) { std::string line; std::getline(str, line); std::stringstream lineStream(line); std::string cell; m_data.clear(); while(std::getline(lineStream, cell, ',')) { m_data.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. m_data.push_back(""); } } private: std::vector 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"; } } 

Но с небольшой работой мы могли бы технически создать iterator:

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

Решение с использованием Boost Tokenizer:

 std::vector vec; using namespace boost; tokenizer > tk( line, escaped_list_separator('\\', ',', '\"')); for (tokenizer >::iterator i(tk.begin()); i!=tk.end();++i) { vec.push_back(*i); } 

Библиотека инструментов C ++ String Toolkit (StrTk) имеет class сетки токенов, который позволяет загружать данные из текстовых файлов, строк или буферов символов и анализировать / обрабатывать их в столбце строк.

Вы можете указать разделители строк и разделители столбцов или просто использовать значения по умолчанию.

 void foo() { std::string data = "1,2,3,4,5\n" "0,2,4,6,8\n" "1,3,5,7,9\n"; strtk::token_grid grid(data,data.size(),","); for(std::size_t i = 0; i < grid.row_count(); ++i) { strtk::token_grid::row_type r = grid.row(i); for(std::size_t j = 0; j < r.size(); ++j) { std::cout << r.get(j) << "\t"; } std::cout << std::endl; } std::cout << std::endl; } 

Дополнительные примеры можно найти здесь

Моя версия не использует ничего, кроме стандартной библиотеки C ++ 11. Он хорошо справляется с цитатой CSV Excel:

 spam eggs,"foo,bar","""fizz buzz""" 1.23,4.567,-8.00E+09 

Код написан как конечная машина и потребляет по одному символу за раз. Думаю, легче рассуждать.

 #include  #include  #include  enum class CSVState { UnquotedField, QuotedField, QuotedQuote }; std::vector readCSVRow(const std::string &row) { CSVState state = CSVState::UnquotedField; std::vector fields {""}; size_t i = 0; // index of the current field for (char c : row) { switch (state) { case CSVState::UnquotedField: switch (c) { case ',': // end of field fields.push_back(""); i++; break; case '"': state = CSVState::QuotedField; break; default: fields[i].push_back(c); break; } break; case CSVState::QuotedField: switch (c) { case '"': state = CSVState::QuotedQuote; break; default: fields[i].push_back(c); break; } break; case CSVState::QuotedQuote: switch (c) { case ',': // , after closing quote fields.push_back(""); i++; state = CSVState::UnquotedField; break; case '"': // "" -> " fields[i].push_back('"'); state = CSVState::QuotedField; break; default: // end of quote state = CSVState::UnquotedField; break; } break; } } return fields; } /// Read CSV file, Excel dialect. Accept "quoted fields ""with quotes""" std::vector> readCSV(std::istream &in) { std::vector> table; std::string row; while (!in.eof()) { std::getline(in, row); if (in.bad() || in.fail()) { break; } auto fields = readCSVRow(row); table.push_back(fields); } return table; } 

Вы можете использовать Boost Tokenizer с escaped_list_separator.

escaped_list_separator анализирует надмножество csv. Повысьте :: Tokenizer

Это использует только заголовочные файлы Boost tokenizer, не связывая их с необходимыми библиотеками.

Вот пример: (см. Файл Parse CSV с Boost::tokenizer C ++ для подробностей или Boost::tokenizer ):

 #include  // cout, endl #include  // fstream #include  #include  #include  // copy #include  // ostream_operator #include  int main() { using namespace std; using namespace boost; string data("data.csv"); ifstream in(data.c_str()); if (!in.is_open()) return 1; typedef tokenizer< escaped_list_separator > Tokenizer; vector< string > vec; string line; while (getline(in,line)) { Tokenizer tok(line); vec.assign(tok.begin(),tok.end()); // vector now contains strings from one row, output to cout here copy(vec.begin(), vec.end(), ostream_iterator(cout, "|")); cout << "\n----------------------" << endl; } } 

Нельзя переусердствовать с использованием Spirit для анализа CSV. Дух хорошо подходит для задач микроанализа. Например, с Spirit 2.1 это так же просто:

 bool r = phrase_parse(first, last, // Begin grammar ( double_ % ',' ) , // End grammar space, v); 

Вектор, v, заполняется значениями. Существует ряд руководств, затрагивающих это в новых документах Spirit 2.1, которые только что были выпущены с Boost 1.41.

Учебник развивается от простого к сложному. Парсеры CSV представлены где-то посередине и затрагивают различные методы использования Духа. Сгенерированный код такой же жесткий, как ручной код. Проверьте сборщик!

Если вы действительно заботитесь о правильном анализе CSV, это будет делать это … относительно медленно, так как он работает по одному символу за раз.

  void ParseCSV(const string& csvSource, vector >& lines) { bool inQuote(false); bool newLine(false); string field; lines.clear(); vector line; string::const_iterator aChar = csvSource.begin(); while (aChar != csvSource.end()) { switch (*aChar) { case '"': newLine = false; inQuote = !inQuote; break; case ',': newLine = false; if (inQuote == true) { field += *aChar; } else { line.push_back(field); field.clear(); } break; case '\n': case '\r': if (inQuote == true) { field += *aChar; } else { if (newLine == false) { line.push_back(field); lines.push_back(line); field.clear(); line.clear(); newLine = true; } } break; default: newLine = false; field.push_back(*aChar); break; } aChar++; } if (field.size()) line.push_back(field); if (line.size()) lines.push_back(line); } 

При использовании Boost Tokenizer escaped_list_separator для CSV-файлов следует знать следующее:

  1. Для этого требуется escape-символ (default back-slash – \)
  2. Для этого требуется разделитель / разделитель-символ (по умолчанию – запятая)
  3. Для этого требуется котировочный символ (по умолчанию – «)

Формат CSV, указанный wiki, указывает, что поля данных могут содержать разделители в кавычках (поддерживаются):

1997, Ford, E350, «Супер, роскошный грузовик»

Формат CSV, указанный wiki, гласит, что одинарные кавычки должны обрабатываться с помощью двойных кавычек (escaped_list_separator удалит все символы кавычек):

1997, Ford, E350, «Супер», «роскошный» грузовик,

Формат CSV не указывает, что любые символы обратной косой черты должны быть удалены (escaped_list_separator удалит все escape-символы).

Возможная обходная ситуация для исправления поведения по умолчанию для ускорения escaped_list_separator:

  1. Сначала замените все символы обратной косой черты (\) двумя символами обратной косой черты (\\), чтобы они не были удалены.
  2. Во-вторых, замените все двойные кавычки (“”) одним символом обратной косой черты и цитатой (\ “)

Эта обходная ситуация имеет побочный эффект, что пустые поля данных, которые представлены двойной кавычкой, будут преобразованы в одноточечный токен. При повторении через токены необходимо проверить, является ли токен одиночной кавычкой, и рассматривать его как пустую строку.

Не очень, но он работает, пока в кавычках нет новых строк.

Возможно, вы захотите посмотреть мой проект FOSS CSVfix ( обновленная ссылка ), который представляет собой редактор streamа CSV, написанный на C ++. Парсер CSV не является призом, но может ли работа и весь пакет делать то, что вам нужно, без написания какого-либо кода.

См. Alib / src / a_csv.cpp для синтаксического анализа CSV и csvlib / src / csved_ioman.cpp ( IOManager::ReadCSV ) для примера использования.

Поскольку все вопросы CSV, похоже, перенаправляются сюда, я думал, что отправлю свой ответ здесь. Этот ответ напрямую не затрагивает вопрос афера. Я хотел иметь возможность читать в streamе, который, как известно, находится в формате CSV, а также типы каждого поля уже были известны. Конечно, приведенный ниже метод может использоваться для обработки каждого поля как строкового типа.

В качестве примера того, как я хотел использовать входной stream CSV, рассмотрим следующий ввод (взятый из страницы Википедии в CSV ):

 const char input[] = "Year,Make,Model,Description,Price\n" "1997,Ford,E350,\"ac, abs, moon\",3000.00\n" "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",\"\",4900.00\n" "1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",\"\",5000.00\n" "1996,Jeep,Grand Cherokee,\"MUST SELL!\n\ air, moon roof, loaded\",4799.00\n" ; 

Затем мне захотелось прочитать данные следующим образом:

 std::istringstream ss(input); std::string title[5]; int year; std::string make, model, desc; float price; csv_istream(ss) >> title[0] >> title[1] >> title[2] >> title[3] >> title[4]; while (csv_istream(ss) >> year >> make >> model >> desc >> price) { //...do something with the record... } 

Это было решение, в котором я закончил.

 struct csv_istream { std::istream &is_; csv_istream (std::istream &is) : is_(is) {} void scan_ws () const { while (is_.good()) { int c = is_.peek(); if (c != ' ' && c != '\t') break; is_.get(); } } void scan (std::string *s = 0) const { std::string ws; int c = is_.get(); if (is_.good()) { do { if (c == ',' || c == '\n') break; if (s) { ws += c; if (c != ' ' && c != '\t') { *s += ws; ws.clear(); } } c = is_.get(); } while (is_.good()); if (is_.eof()) is_.clear(); } } template  struct set_value { void operator () (std::string in, T &v) const { std::istringstream(in) >> v; } }; template  struct set_value { template  void convert (std::string in, T &v) const { if (SIGNED) v = ::strtoll(in.c_str(), 0, 0); else v = ::strtoull(in.c_str(), 0, 0); } void operator () (std::string in, T &v) const { convert::val>(in, v); } }; template  const csv_istream & operator >> (T &v) const { std::string tmp; scan(&tmp); set_value::val>()(tmp, v); return *this; } const csv_istream & operator >> (std::string &v) const { v.clear(); scan_ws(); if (is_.peek() != '"') scan(&v); else { std::string tmp; is_.get(); std::getline(is_, tmp, '"'); while (is_.peek() == '"') { v += tmp; v += is_.get(); std::getline(is_, tmp, '"'); } v += tmp; scan(); } return *this; } template  const csv_istream & operator >> (T &(*manip)(T &)) const { is_ >> manip; return *this; } operator bool () const { return !is_.fail(); } }; 

С помощью следующих помощников, которые могут быть упрощены с помощью новых шаблонов интегральных признаков в C ++ 11:

 template  struct is_signed_int { enum { val = false }; }; template <> struct is_signed_int { enum { val = true}; }; template <> struct is_signed_int { enum { val = true}; }; template <> struct is_signed_int { enum { val = true}; }; template <> struct is_signed_int { enum { val = true}; }; template  struct is_unsigned_int { enum { val = false }; }; template <> struct is_unsigned_int { enum { val = true}; }; template <> struct is_unsigned_int { enum { val = true}; }; template <> struct is_unsigned_int { enum { val = true}; }; template <> struct is_unsigned_int { enum { val = true}; }; template  struct is_int { enum { val = (is_signed_int::val || is_unsigned_int::val) }; }; 

Другая библиотека ввода / вывода CSV находится здесь:

http://code.google.com/p/fast-cpp-csv-parser/

 #include "csv.h" int main(){ io::CSVReader<3> in("ram.csv"); in.read_header(io::ignore_extra_column, "vendor", "size", "speed"); std::string vendor; int size; double speed; while(in.read_row(vendor, size, speed)){ // do stuff with the data } } 

Другое решение похоже на ответ Локи Астари , в C ++ 11. Строки здесь: std::tuple s данного типа. Код сканирует одну строку, затем сканирует до каждого разделителя, а затем преобразует и выгружает значение непосредственно в кортеж (с небольшим количеством кода шаблона).

 for (auto row : csv(file, ',')) { std::cout << "first col: " << std::get<0>(row) << std::endl; } 

Advanges:

  • довольно чистый и простой в использовании, только C ++ 11.
  • автоматическое преобразование типов в std::tuple через operator>> .

Чего не хватает:

  • ускорение и цитирование
  • без обработки ошибок в случае неправильного CSV.

Основной код:

 #include  #include  #include  namespace csvtools { /// Read the last element of the tuple without calling recursively template  typename std::enable_if= std::tuple_size>::value - 1>::type read_tuple(std::istream &in, std::tuple &out, const char delimiter) { std::string cell; std::getline(in, cell, delimiter); std::stringstream cell_stream(cell); cell_stream >> std::get(out); } /// Read the @p idx-th element of the tuple and then calls itself with @p idx + 1 to /// read the next element of the tuple. Automatically falls in the previous case when /// reaches the last element of the tuple thanks to enable_if template  typename std::enable_if>::value - 1>::type read_tuple(std::istream &in, std::tuple &out, const char delimiter) { std::string cell; std::getline(in, cell, delimiter); std::stringstream cell_stream(cell); cell_stream >> std::get(out); read_tuple(in, out, delimiter); } } /// Iterable csv wrapper around a stream. @p fields the list of types that form up a row. template  class csv { std::istream &_in; const char _delim; public: typedef std::tuple value_type; class iterator; /// Construct from a stream. inline csv(std::istream &in, const char delim) : _in(in), _delim(delim) {} /// Status of the underlying stream /// @{ inline bool good() const { return _in.good(); } inline const std::istream &underlying_stream() const { return _in; } /// @} inline iterator begin(); inline iterator end(); private: /// Reads a line into a stringstream, and then reads the line into a tuple, that is returned inline value_type read_row() { std::string line; std::getline(_in, line); std::stringstream line_stream(line); std::tuple retval; csvtools::read_tuple<0, fields...>(line_stream, retval, _delim); return retval; } }; /// Iterator; just calls recursively @ref csv::read_row and stores the result. template  class csv::iterator { csv::value_type _row; csv *_parent; public: typedef std::input_iterator_tag iterator_category; typedef csv::value_type value_type; typedef std::size_t difference_type; typedef csv::value_type * pointer; typedef csv::value_type & reference; /// Construct an empty/end iterator inline iterator() : _parent(nullptr) {} /// Construct an iterator at the beginning of the @p parent csv object. inline iterator(csv &parent) : _parent(parent.good() ? &parent : nullptr) { ++(*this); } /// Read one row, if possible. Set to end if parent is not good anymore. inline iterator &operator++() { if (_parent != nullptr) { _row = _parent->read_row(); if (!_parent->good()) { _parent = nullptr; } } return *this; } inline iterator operator++(int) { iterator copy = *this; ++(*this); return copy; } inline csv::value_type const &operator*() const { return _row; } inline csv::value_type const *operator->() const { return &_row; } bool operator==(iterator const &other) { return (this == &other) or (_parent == nullptr and other._parent == nullptr); } bool operator!=(iterator const &other) { return not (*this == other); } }; template  typename csv::iterator csv::begin() { return iterator(*this); } template  typename csv::iterator csv::end() { return iterator(); } 

Я поставил крошечный рабочий пример на GitHub ; Я использовал его для parsingа некоторых числовых данных, и он служил своей цели.

Вот еще одна реализация парсера Unicode CSV (работает с wchar_t). Я написал часть этого, в то время как Джонатан Леффлер написал все остальное.

Примечание. Этот синтаксический анализатор нацелен на максимально возможное дублирование поведения Excel, особенно при импорте поврежденных или искаженных CSV-файлов.

Это оригинальный вопрос – анализ CSV-файла с многострочными полями и двойными кавычками

Это код как SSCCE (Short, Self-Contained, Correct Example).

 #include  #include  #include  extern const wchar_t *nextCsvField(const wchar_t *p, wchar_t sep, bool *newline); // Returns a pointer to the start of the next field, // or zero if this is the last field in the CSV // p is the start position of the field // sep is the separator used, ie comma or semicolon // newline says whether the field ends with a newline or with a comma const wchar_t *nextCsvField(const wchar_t *p, wchar_t sep, bool *newline) { // Parse quoted sequences if ('"' == p[0]) { p++; while (1) { // Find next double-quote p = wcschr(p, L'"'); // If we don't find it or it's the last symbol // then this is the last field if (!p || !p[1]) return 0; // Check for "", it is an escaped double-quote if (p[1] != '"') break; // Skip the escaped double-quote p += 2; } } // Find next newline or comma. wchar_t newline_or_sep[4] = L"\n\r "; newline_or_sep[2] = sep; p = wcspbrk(p, newline_or_sep); // If no newline or separator, this is the last field. if (!p) return 0; // Check if we had newline. *newline = (p[0] == '\r' || p[0] == '\n'); // Handle "\r\n", otherwise just increment if (p[0] == '\r' && p[1] == '\n') p += 2; else p++; return p; } static wchar_t *csvFieldData(const wchar_t *fld_s, const wchar_t *fld_e, wchar_t *buffer, size_t buflen) { wchar_t *dst = buffer; wchar_t *end = buffer + buflen - 1; const wchar_t *src = fld_s; if (*src == L'"') { const wchar_t *p = src + 1; while (p < fld_e && dst < end) { if (p[0] == L'"' && p+1 < fld_s && p[1] == L'"') { *dst++ = p[0]; p += 2; } else if (p[0] == L'"') { p++; break; } else *dst++ = *p++; } src = p; } while (src < fld_e && dst < end) *dst++ = *src++; if (dst >= end) return 0; *dst = L'\0'; return(buffer); } static void dissect(const wchar_t *line) { const wchar_t *start = line; const wchar_t *next; bool eol; wprintf(L"Input %3zd: [%.*ls]\n", wcslen(line), wcslen(line)-1, line); while ((next = nextCsvField(start, L',', &eol)) != 0) { wchar_t buffer[1024]; wprintf(L"Raw Field: [%.*ls] (eol = %d)\n", (next - start - eol), start, eol); if (csvFieldData(start, next-1, buffer, sizeof(buffer)/sizeof(buffer[0])) != 0) wprintf(L"Field %3zd: [%ls]\n", wcslen(buffer), buffer); start = next; } } static const wchar_t multiline[] = L"First field of first row,\"This field is multiline\n" "\n" "but that's OK because it's enclosed in double quotes, and this\n" "is an escaped \"\" double quote\" but this one \"\" is not\n" " \"This is second field of second row, but it is not multiline\n" " because it doesn't start \n" " with an immediate double quote\"\n" ; int main(void) { wchar_t line[1024]; while (fgetws(line, sizeof(line)/sizeof(line[0]), stdin)) dissect(line); dissect(multiline); return 0; } 

Первое, что вам нужно сделать, это убедиться, что файл существует. Для этого вам просто нужно попытаться открыть stream файлов на пути. После того, как вы открыли stream файлов, используйте stream.fail (), чтобы увидеть, работает ли он, как ожидалось, или нет.

 bool fileExists(string fileName) { ifstream test; test.open(fileName.c_str()); if (test.fail()) { test.close(); return false; } else { test.close(); return true; } } 

Вы также должны убедиться, что указанный файл является правильным типом файла. Для этого вам нужно просмотреть путь к файлу, пока не найдете расширение файла. После расширения файла убедитесь, что он является CSV-файлом.

 bool verifyExtension(string filename) { int period = 0; for (unsigned int i = 0; i < filename.length(); i++) { if (filename[i] == '.') period = i; } string extension; for (unsigned int i = period; i < filename.length(); i++) extension += filename[i]; if (extension == ".csv") return true; else return false; } 

Эта функция вернет расширение файла, которое будет использоваться позже в сообщении об ошибке.

 string getExtension(string filename) { int period = 0; for (unsigned int i = 0; i < filename.length(); i++) { if (filename[i] == '.') period = i; } string extension; if (period != 0) { for (unsigned int i = period; i < filename.length(); i++) extension += filename[i]; } else extension = "NO FILE"; return extension; } 

Эта функция фактически вызовет проверки ошибок, созданные выше, а затем проанализирует файл.

 void parseFile(string fileName) { if (fileExists(fileName) && verifyExtension(fileName)) { ifstream fs; fs.open(fileName.c_str()); string fileCommand; while (fs.good()) { string temp; getline(fs, fileCommand, '\n'); for (unsigned int i = 0; i < fileCommand.length(); i++) { if (fileCommand[i] != ',') temp += fileCommand[i]; else temp += " "; } if (temp != "\0") { // Place your code here to run the file. } } fs.close(); } else if (!fileExists(fileName)) { cout << "Error: The provided file does not exist: " << fileName << endl; if (!verifyExtension(fileName)) { if (getExtension(fileName) != "NO FILE") cout << "\tCheck the file extension." << endl; else cout << "\tThere is no file in the provided path." << endl; } } else if (!verifyExtension(fileName)) { if (getExtension(fileName) != "NO FILE") cout << "Incorrect file extension provided: " << getExtension(fileName) << endl; else cout << "There is no file in the following path: " << fileName << endl; } } 

I wrote a header-only, C++11 CSV parser . It’s well tested, fast, supports the entire CSV spec (quoted fields, delimiter/terminator in quotes, quote escaping, etc.), and is configurable to account for the CSVs that don’t adhere to the specification.

Configuration is done through a fluent interface:

 // constructor accepts any input stream CsvParser parser = CsvParser(std::cin) .delimiter(';') // delimited by ; instead of , .quote('\'') // quoted fields use ' instead of " .terminator('\0'); // terminated by \0 instead of by \r\n, \n, or \r 

Parsing is just a range based for loop:

 #include  #include "../parser.hpp" using namespace aria::csv; int main() { std::ifstream f("some_file.csv"); CsvParser parser(f); for (auto& row : parser) { for (auto& field : row) { std::cout << field << " | "; } std::cout << std::endl; } } 

Excuse me, but this all seems like a great deal of elaborate syntax to hide a few lines of code.

Why not this:

 /** Read line from a CSV file @param[in] fp file pointer to open file @param[in] vls reference to vector of strings to hold next line */ void readCSV( FILE *fp, std::vector& vls ) { vls.clear(); if( ! fp ) return; char buf[10000]; if( ! fgets( buf,999,fp) ) return; std::string s = buf; int p,q; q = -1; // loop over columns while( 1 ) { p = q; q = s.find_first_of(",\n",p+1); if( q == -1 ) break; vls.push_back( s.substr(p+1,qp-1) ); } } int _tmain(int argc, _TCHAR* argv[]) { std::vector vls; FILE * fp = fopen( argv[1], "r" ); if( ! fp ) return 1; readCSV( fp, vls ); readCSV( fp, vls ); readCSV( fp, vls ); std::cout << "row 3, col 4 is " << vls[3].c_str() << "\n"; return 0; } 

Here is code for reading a matrix, note you also have a csvwrite function in matlab

 void loadFromCSV( const std::string& filename ) { std::ifstream file( filename.c_str() ); std::vector< std::vector > matrix; std::vector row; std::string line; std::string cell; while( file ) { std::getline(file,line); std::stringstream lineStream(line); row.clear(); while( std::getline( lineStream, cell, ',' ) ) row.push_back( cell ); if( !row.empty() ) matrix.push_back( row ); } for( int i=0; i 

You can open and read .csv file using fopen ,fscanf functions ,but the important thing is to parse the data.Simplest way to parse the data using delimiter.In case of .csv , delimiter is ‘,’.

Suppose your data1.csv file is as follows :

 A,45,76,01 B,77,67,02 C,63,76,03 D,65,44,04 

you can tokenize data and store in char array and later use atoi() etc function for appropriate conversions

 FILE *fp; char str1[10], str2[10], str3[10], str4[10]; fp = fopen("G:\\data1.csv", "r"); if(NULL == fp) { printf("\nError in opening file."); return 0; } while(EOF != fscanf(fp, " %[^,], %[^,], %[^,], %s, %s, %s, %s ", str1, str2, str3, str4)) { printf("\n%s %s %s %s", str1, str2, str3, str4); } fclose(fp); 

[^,], ^ -it inverts logic , means match any string that does not contain comma then last , says to match comma that terminated previous string.

You gotta feel proud when you use something so beautiful as boost::spirit

Here my attempt of a parser (almost) complying with the CSV specifications on this link CSV specs (I didn’t need line breaks within fields. Also the spaces around the commas are dismissed).

After you overcome the shocking experience of waiting 10 seconds for compiling this code :), you can sit back and enjoy.

 // csvparser.cpp #include  #include  #include  #include  namespace qi = boost::spirit::qi; namespace bascii = boost::spirit::ascii; template  struct csv_parser : qi::grammar(), bascii::space_type> { qi::rule COMMA; qi::rule DDQUOTE; qi::rule non_escaped; qi::rule escaped; qi::rule field; qi::rule(), bascii::space_type > start; csv_parser() : csv_parser::base_type(start) { using namespace qi; using qi::lit; using qi::lexeme; using bascii::char_; start = field % ','; field = escaped | non_escaped; escaped = lexeme['"' >> *( char_ -(char_('"') | ',') | COMMA | DDQUOTE) >> '"']; non_escaped = lexeme[ *( char_ -(char_('"') | ',') ) ]; DDQUOTE = lit("\"\"") [_val = '"']; COMMA = lit(",") [_val = ',']; } }; int main() { std::cout << "Enter CSV lines [empty] to quit\n"; using bascii::space; typedef std::string::const_iterator iterator_type; typedef csv_parser csv_parser; csv_parser grammar; std::string str; int fid; while (getline(std::cin, str)) { fid = 0; if (str.empty()) break; std::vector csv; std::string::const_iterator it_beg = str.begin(); std::string::const_iterator it_end = str.end(); bool r = phrase_parse(it_beg, it_end, grammar, space, csv); if (r && it_beg == it_end) { std::cout << "Parsing succeeded\n"; for (auto& field: csv) { std::cout << "field " << ++fid << ": " << field << std::endl; } } else { std::cout << "Parsing failed\n"; } } return 0; } 

Compile:

 make csvparser 

Test (example stolen from Wikipedia ):

 ./csvparser Enter CSV lines [empty] to quit 1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00 Parsing succeeded field 1: 1999 field 2: Chevy field 3: Venture "Extended Edition, Very Large" field 4: field 5: 5000.00 1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00" Parsing failed 

This solution detects these 4 cases

complete class is at

https://github.com/pedro-vicente/csv-parser

 1,field 2,field 3, 1,field 2,"field 3 quoted, with separator", 1,field 2,"field 3 with newline", 1,field 2,"field 3 with newline and separator,", 

It reads the file character by character, and reads 1 row at a time to a vector (of strings), therefore suitable for very large files.

Usage is

Iterate until an empty row is returned (end of file). A row is a vector where each entry is a CSV column.

 read_csv_t csv; csv.open("../test.csv"); std::vector row; while (true) { row = csv.read_row(); if (row.size() == 0) { break; } } 

the class declaration

 class read_csv_t { public: read_csv_t(); int open(const std::string &file_name); std::vector read_row(); private: std::ifstream m_ifs; }; 

the implementation

 std::vector read_csv_t::read_row() { bool quote_mode = false; std::vector row; std::string column; char c; while (m_ifs.get(c)) { switch (c) { ///////////////////////////////////////////////////////////////////////////////////////////////////// //separator ',' detected. //in quote mode add character to column //push column if not in quote mode ///////////////////////////////////////////////////////////////////////////////////////////////////// case ',': if (quote_mode == true) { column += c; } else { row.push_back(column); column.clear(); } break; ///////////////////////////////////////////////////////////////////////////////////////////////////// //quote '"' detected. //toggle quote mode ///////////////////////////////////////////////////////////////////////////////////////////////////// case '"': quote_mode = !quote_mode; break; ///////////////////////////////////////////////////////////////////////////////////////////////////// //line end detected //in quote mode add character to column //return row if not in quote mode ///////////////////////////////////////////////////////////////////////////////////////////////////// case '\n': case '\r': if (quote_mode == true) { column += c; } else { return row; } break; ///////////////////////////////////////////////////////////////////////////////////////////////////// //default, add character to column ///////////////////////////////////////////////////////////////////////////////////////////////////// default: column += c; break; } } //return empty vector if end of file detected m_ifs.close(); std::vector v; return v; } 

You could also take a look at capabilities of Qt library.

It has regular expressions support and QString class has nice methods, eg split() returning QStringList, list of strings obtained by splitting the original string with a provided delimiter. Should suffice for csv file..

To get a column with a given header name I use following: c++ inheritance Qt problem qstring

If you don’t want to deal with including boost in your project (it is considerably large if all you are going to use it for is CSV parsing…)

I have had luck with the CSV parsing here:

http://www.zedwood.com/article/112/cpp-csv-parser

It handles quoted fields – but does not handle inline \n characters (which is probably fine for most uses).

This is an old thread but its still at the top of search results, so I’m adding my solution using std::stringstream and a simple string replace method by Yves Baumes I found here.

The following example will read a file line by line, ignore comment lines starting with // and parse the other lines into a combination of strings, ints and doubles. Stringstream does the parsing, but expects fields to be delimited by whitespace, so I use stringreplace to turn commas into spaces first. It handles tabs ok, but doesn’t deal with quoted strings.

Bad or missing input is simply ignored, which may or may not be good, depending on your circumstance.

 #include  #include  #include  void StringReplace(std::string& str, const std::string& oldStr, const std::string& newStr) // code by Yves Baumes // http://stackoverflow.com/questions/1494399/how-do-i-search-find-and-replace-in-a-standard-string { size_t pos = 0; while((pos = str.find(oldStr, pos)) != std::string::npos) { str.replace(pos, oldStr.length(), newStr); pos += newStr.length(); } } void LoadCSV(std::string &filename) { std::ifstream stream(filename); std::string in_line; std::string Field; std::string Chan; int ChanType; double Scale; int Import; while (std::getline(stream, in_line)) { StringReplace(in_line, ",", " "); std::stringstream line(in_line); line >> Field >> Chan >> ChanType >> Scale >> Import; if (Field.substr(0,2)!="//") { // do your stuff // this is CBuilder code for demonstration, sorry ShowMessage((String)Field.c_str() + "\n" + Chan.c_str() + "\n" + IntToStr(ChanType) + "\n" +FloatToStr(Scale) + "\n" +IntToStr(Import)); } } } 

For what it is worth, here is my implementation. It deals with wstring input, but could be adjusted to string easily. It does not handle newline in fields (as my application does not either, but adding its support isn’t too difficult) and it does not comply with “\r\n” end of line as per RFC (assuming you use std::getline), but it does handle whitespace trimming and double-quotes correctly (hopefully).

 using namespace std; // trim whitespaces around field or double-quotes, remove double-quotes and replace escaped double-quotes (double double-quotes) wstring trimquote(const wstring& str, const wstring& whitespace, const wchar_t quotChar) { wstring ws; wstring::size_type strBegin = str.find_first_not_of(whitespace); if (strBegin == wstring::npos) return L""; wstring::size_type strEnd = str.find_last_not_of(whitespace); wstring::size_type strRange = strEnd - strBegin + 1; if((str[strBegin] == quotChar) && (str[strEnd] == quotChar)) { ws = str.substr(strBegin+1, strRange-2); strBegin = 0; while((strEnd = ws.find(quotChar, strBegin)) != wstring::npos) { ws.erase(strEnd, 1); strBegin = strEnd+1; } } else ws = str.substr(strBegin, strRange); return ws; } pair nextCSVQuotePair(const wstring& line, const wchar_t quotChar, unsigned ofs = 0) { pair r; r.first = line.find(quotChar, ofs); r.second = wstring::npos; if(r.first != wstring::npos) { r.second = r.first; while(((r.second = line.find(quotChar, r.second+1)) != wstring::npos) && (line[r.second+1] == quotChar)) // WARNING: assumes null-terminated string such that line[r.second+1] always exist r.second++; } return r; } unsigned parseLine(vector& fields, const wstring& line) { unsigned ofs, ofs0, np; const wchar_t delim = L','; const wstring whitespace = L" \t\xa0\x3000\x2000\x2001\x2002\x2003\x2004\x2005\x2006\x2007\x2008\x2009\x200a\x202f\x205f"; const wchar_t quotChar = L'\"'; pair quot; fields.clear(); ofs = ofs0 = 0; quot = nextCSVQuotePair(line, quotChar); while((np = line.find(delim, ofs)) != wstring::npos) { if((np > quot.first) && (np < quot.second)) { // skip delimiter inside quoted field ofs = quot.second+1; quot = nextCSVQuotePair(line, quotChar, ofs); continue; } fields.push_back( trimquote(line.substr(ofs0, np-ofs0), whitespace, quotChar) ); ofs = ofs0 = np+1; } fields.push_back( trimquote(line.substr(ofs0), whitespace, quotChar) ); return fields.size(); } 

Here is a ready-to use function if all you need is to load a data file of doubles (no integers, no text).

 #include  #include  #include  #include  #include  #include  using namespace std; /** * Parse a CSV data file and fill the 2d STL vector "data". * Limits: only "pure datas" of doubles, not encapsulated by " and without \n inside. * Further no formatting in the data (eg scientific notation) * It however handles both dots and commas as decimal separators and removes thousand separator. * * returnCodes[0]: file access 0-> ok 1-> not able to read; 2-> decimal separator equal to comma separator * returnCodes[1]: number of records * returnCodes[2]: number of fields. -1 If rows have different field size * */ vector readCsvData (vector >& data, const string& filename, const string& delimiter, const string& decseparator){ int vv[3] = { 0,0,0 }; vector returnCodes(&vv[0], &vv[0]+3); string rowstring, stringtoken; double doubletoken; int rowcount=0; int fieldcount=0; data.clear(); ifstream iFile(filename, ios_base::in); if (!iFile.is_open()){ returnCodes[0] = 1; return returnCodes; } while (getline(iFile, rowstring)) { if (rowstring=="") continue; // empty line rowcount ++; //let's start with 1 if(delimiter == decseparator){ returnCodes[0] = 2; return returnCodes; } if(decseparator != "."){ // remove dots (used as thousand separators) string::iterator end_pos = remove(rowstring.begin(), rowstring.end(), '.'); rowstring.erase(end_pos, rowstring.end()); // replace decimal separator with dots. replace(rowstring.begin(), rowstring.end(),decseparator.c_str()[0], '.'); } else { // remove commas (used as thousand separators) string::iterator end_pos = remove(rowstring.begin(), rowstring.end(), ','); rowstring.erase(end_pos, rowstring.end()); } // tokenize.. vector tokens; // Skip delimiters at beginning. string::size_type lastPos = rowstring.find_first_not_of(delimiter, 0); // Find first "non-delimiter". string::size_type pos = rowstring.find_first_of(delimiter, lastPos); while (string::npos != pos || string::npos != lastPos){ // Found a token, convert it to double add it to the vector. stringtoken = rowstring.substr(lastPos, pos - lastPos); if (stringtoken == "") { tokens.push_back(0.0); } else { istringstream totalSString(stringtoken); totalSString >> doubletoken; tokens.push_back(doubletoken); } // Skip delimiters. Note the "not_of" lastPos = rowstring.find_first_not_of(delimiter, pos); // Find next "non-delimiter" pos = rowstring.find_first_of(delimiter, lastPos); } if(rowcount == 1){ fieldcount = tokens.size(); returnCodes[2] = tokens.size(); } else { if ( tokens.size() != fieldcount){ returnCodes[2] = -1; } } data.push_back(tokens); } iFile.close(); returnCodes[1] = rowcount; return returnCodes; } 

Another quick and easy way is to use Boost.Fusion I/O :

 #include  #include  #include  #include  namespace fusion = boost::fusion; struct CsvString { std::string value; // Stop reading a string once a CSV delimeter is encountered. friend std::istream& operator>>(std::istream& s, CsvString& v) { v.value.clear(); for(;;) { auto c = s.peek(); if(std::istream::traits_type::eof() == c || ',' == c || '\n' == c) break; v.value.push_back(c); s.get(); } return s; } friend std::ostream& operator<<(std::ostream& s, CsvString const& v) { return s << v.value; } }; int main() { std::stringstream input("abc,123,true,3.14\n" "def,456,false,2.718\n"); typedef boost::tuple CsvRow; using fusion::operator<<; std::cout << std::boolalpha; using fusion::operator>>; input >> std::boolalpha; input >> fusion::tuple_open("") >> fusion::tuple_close("\n") >> fusion::tuple_delimiter(','); for(CsvRow row; input >> row;) std::cout << row << '\n'; } 

Выходы:

 (abc 123 true 3.14) (def 456 false 2.718) 

I wrote a nice way of parsing CSV files and I thought I should add it as an answer:

 #include  #include  #include  #include  #include  struct CSVDict { std::vector< std::string > inputImages; std::vector< double > inputLabels; }; /** \brief Splits the string \param str String to split \param delim Delimiter on the basis of which splitting is to be done \return results Output in the form of vector of strings */ std::vector stringSplit( const std::string &str, const std::string &delim ) { std::vector results; for (size_t i = 0; i < str.length(); i++) { std::string tempString = ""; while ((str[i] != *delim.c_str()) && (i < str.length())) { tempString += str[i]; i++; } results.push_back(tempString); } return results; } /** \brief Parse the supplied CSV File and obtain Row and Column information. Assumptions: 1. Header information is in first row 2. Delimiters are only used to differentiate cell members \param csvFileName The full path of the file to parse \param inputColumns The string of input columns which contain the data to be used for further processing \param inputLabels The string of input labels based on which further processing is to be done \param delim The delimiters used in inputColumns and inputLabels \return Vector of Vector of strings: Collection of rows and columns */ std::vector< CSVDict > parseCSVFile( const std::string &csvFileName, const std::string &inputColumns, const std::string &inputLabels, const std::string &delim ) { std::vector< CSVDict > return_CSVDict; std::vector< std::string > inputColumnsVec = stringSplit(inputColumns, delim), inputLabelsVec = stringSplit(inputLabels, delim); std::vector< std::vector< std::string > > returnVector; std::ifstream inFile(csvFileName.c_str()); int row = 0; std::vector< size_t > inputColumnIndeces, inputLabelIndeces; for (std::string line; std::getline(inFile, line, '\n');) { CSVDict tempDict; std::vector< std::string > rowVec; line.erase(std::remove(line.begin(), line.end(), '"'), line.end()); rowVec = stringSplit(line, delim); // for the first row, record the indeces of the inputColumns and inputLabels if (row == 0) { for (size_t i = 0; i < rowVec.size(); i++) { for (size_t j = 0; j < inputColumnsVec.size(); j++) { if (rowVec[i] == inputColumnsVec[j]) { inputColumnIndeces.push_back(i); } } for (size_t j = 0; j < inputLabelsVec.size(); j++) { if (rowVec[i] == inputLabelsVec[j]) { inputLabelIndeces.push_back(i); } } } } else { for (size_t i = 0; i < inputColumnIndeces.size(); i++) { tempDict.inputImages.push_back(rowVec[inputColumnIndeces[i]]); } for (size_t i = 0; i < inputLabelIndeces.size(); i++) { double test = std::atof(rowVec[inputLabelIndeces[i]].c_str()); tempDict.inputLabels.push_back(std::atof(rowVec[inputLabelIndeces[i]].c_str())); } return_CSVDict.push_back(tempDict); } row++; } return return_CSVDict; } 

It is possible to use std::regex .

Depending on the size of your file and the memory available to you , it is possible read it either line by line or entirely in an std::string .

To read the file one can use :

 std::ifstream t("file.txt"); std::string sin((std::istreambuf_iterator(t)), std::istreambuf_iterator()); 

then you can match with this which is actually customizable to your needs.

 std::regex word_regex(",\\s]+"); auto what = std::sregex_iterator(sin.begin(), sin.end(), word_regex); auto wend = std::sregex_iterator(); std::vector v; for (;what!=wend ; wend) { std::smatch match = *what; v.push_back(match.str()); } 

Since i’m not used to boost right now, I will suggest a more simple solution. Lets suppose that your .csv file has 100 lines with 10 numbers in each line separated by a ‘,’. You could load this data in the form of an array with the following code:

 #include  #include  #include  #include  using namespace std; int main() { int A[100][10]; ifstream ifs; ifs.open("name_of_file.csv"); string s1; char c; for(int k=0; k<100; k++) { getline(ifs,s1); stringstream stream(s1); int j=0; while(1) { stream >>A[k][j]; stream >> c; j++; if(!stream) {break;} } } } 

I needed an easy-to-use C++ library for parsing CSV files but couldn’t find any available, so I ended up building one. Rapidcsv is a C++11 header-only library which gives direct access to parsed columns (or rows) as vectors, in datatype of choice. Например:

 #include  #include  #include  int main() { rapidcsv::Document doc("../tests/msft.csv"); std::vector close = doc.GetColumn("Close"); std::cout << "Read " << close.size() << " values." << std::endl; } 
  • Подпиксельный сглаженный текст в элементе canvasа HTML5
  • Вертикальная (повернутая) метка в Android
  • Извлечение текста OpenCV
  • Можно ли изменить цвет текста в строке на несколько цветов в Java?
  • Тайм-аут в async / wait
  • Java отрицательный int для шестнадцатеричного и обратного сбоев
  • Форматирование текста в ярлыке WinForm
  • Усечение длинных строк с помощью CSS: возможно?
  • Как добавить разрыв строки в Android TextView?
  • Обосновать последнюю строку div?
  • Могу ли я обернуть каждую строку многострочного текста в промежутке?
  • Interesting Posts

    Linux-дистрибутив с только занятым полем и bash?

    Сохранить данные на арабском языке в базе данных MySQL

    Vim: Как установить предпочтение для пользовательских типов файлов?

    Проблема с вариацией C #: присвоение списка в виде списка

    Удал ли файл .vbox в некоторой версии VirtualBox до 4.1.2?

    Стандартная библиотека C ++: как писать обертки для cout, cerr, cin и endl?

    Как установить наушники на моно в Windows 7?

    Можно ли отправить массив с расширением Postman Chrome?

    Как включить / выключить режим разработки ReactJS ‘?

    Отправка файла Multipart в виде параметров POST с помощью запросов RestTemplate

    Секундомер против использования System.DateTime.Now для событий синхронизации

    Байт-массив для преобразования изображений

    Как добавить вывод в конец текстового файла в сценарии оболочки / bash

    Должен ли код JQuery идти в верхнем или нижнем колонтитуле?

    В Java, как эффективно и элегантно потопить потомков узла дерева?

    Давайте будем гением компьютера.