Getline не читает полную строку CSV-файла C ++ [дубликат]

206
задан Shog9 7 April 2014 в 15:39
поделиться

32 ответа

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

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

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

#include <iterator>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

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<std::string>    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";
    }
}

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

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";
    }
}
231
ответ дан Martin York 18 August 2018 в 05:55
поделиться
  • 1
    first () next (). Что это за Java! Просто шучу. – Martin York 14 July 2009 в 06:15
  • 2
    или вы можете использовать некоторые библиотеки boost для синтаксического анализа csv ... см. ниже – stefanB 21 March 2010 в 01:15
  • 3
    @DarthVader: оверлейное широкое утверждение, которое по своей широте глупо. Если вы хотите уточнить, почему это плохо, а затем почему это плохое применимо в этом контексте. – Martin York 12 January 2012 в 22:10
  • 4
    @DarthVader: Я думаю, что глупо делать широкие обобщения. Приведенный выше код работает правильно, поэтому я могу увидеть что-то не так. Но если у вас есть какой-либо конкретный комментарий к вышеизложенному, я обязательно рассмотрю в этом контексте. Но я вижу, как вы можете прийти к такому выводу, бездумно следуя набору обобщенных правил для C # и применяя его к другому языку. – Martin York 12 January 2012 в 23:29
  • 5
    – sebastian_k 28 June 2013 в 01:58
  • 6
    Это самый простой и чистый пример того, как сделать класс итератора, который я когда-либо видел. – Giancarlo Sportelli 18 February 2015 в 10:25

Вы можете открыть и прочитать файл .csv, используя функции fopen, fscanf, но важно проанализировать данные. Самый простой способ проанализировать данные с помощью разделителя. В случае .csv разделитель равен ','.

Предположим, что ваш файл data1.csv выглядит следующим образом:

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

вы можете токенизировать данные и хранить в массиве символов, а затем использовать функцию atoi () и т. д. для соответствующих преобразований

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 инвертирует логику, означает соответствие любой строке, которая не содержит запятой, а затем последней, говорит, чтобы совместить запятую, завершившую предыдущую строку.

2
ответ дан Amruta Ghodke 18 August 2018 в 05:55
поделиться

Вот готовая к использованию функция, если вам нужно всего лишь загрузить файл данных с удвоением (без целых чисел, без текста).

#include <sstream>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
#include <algorithm>

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 (e.g. 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<int>
readCsvData (vector <vector <double>>& data, const string& filename, const string& delimiter, const string& decseparator){

 int vv[3] = { 0,0,0 };
 vector<int> 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<double> 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;
}
1
ответ дан Antonello 18 August 2018 в 05:55
поделиться

Можно использовать std::regex.

В зависимости от размера вашего файла и доступной вам памяти, можно прочитать его либо по строкам, либо целиком в файле std::string.

Чтобы прочитать файл , вы можете использовать:

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

, после чего вы можете сопоставить это, которое на самом деле настраивается для ваших нужд.

std::regex word_regex(",\\s]+");
auto what = 
    std::sregex_iterator(sin.begin(), sin.end(), word_regex);
auto wend = std::sregex_iterator();

std::vector<std::string> v;
for (;what!=wend ; wend) {
    std::smatch match = *what;
    v.push_back(match.str());
}
5
ответ дан Community 18 August 2018 в 05:55
поделиться
  • 1
    это не будет обрабатывать запятые в кавычках – Ezee 26 March 2015 в 07:41
  • 2
    Вы можете не заботиться о встраивании, потому что большинство компиляторов сами это решают. По крайней мере, я уверен в Visual C ++. Он может встроить метод независимо от вашей спецификации метода. – MrPisarik 29 January 2016 в 16:56
  • 3
    Именно поэтому я их четко обозначил. Gcc и Clang, которые я использую в основном, имеют свои собственные соглашения. A "inline" ключевое слово должно быть просто стимулом. – Pietro Saccardi 29 January 2016 в 16:59

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

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

7
ответ дан cxw 18 August 2018 в 05:55
поделиться
  • 1
    Кажется большим ... Как насчет статуса бета / производства? – neuro 13 July 2009 в 16:30
  • 2
    Статус «развивается», как указано в номерах версий. Мне действительно нужно больше ресурсов от пользователей до перехода на версию 1.0. Плюс у меня есть еще пара функций, которые я хочу добавить, чтобы делать с XML-продукцией из CSV. – user 13 July 2009 в 16:36
  • 3
    Закладка его, и даст ему попробовать в следующий раз, когда мне приходится иметь дело с этими замечательными стандартными файлами CSV ... – neuro 13 July 2009 в 16:44

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

#include <iostream>
#include <vector>
#include <rapidcsv.h>

int main()
{
  rapidcsv::Document doc("../tests/msft.csv");

  std::vector<float> close = doc.GetColumn<float>("Close");
  std::cout << "Read " << close.size() << " values." << std::endl;
}
1
ответ дан d99kris 18 August 2018 в 05:55
поделиться
  • 1
    Хорошая работа, но библиотека не работает должным образом, если заголовок имеет пустые метки. Это типично для таблицы Excel / LibreOffice NxN. Кроме того, он может пропустить последнюю строку данных. К сожалению, ваша библиотека не является надежной. – Maksym Ganenko 23 July 2017 в 10:46
  • 2
    Спасибо за отзыв @ Максим Ганенко, я исправил «последнюю строку данных». ошибка для окончательных строк без прерывания строки. Что касается другой упомянутой проблемы - «заголовки с пустыми метками», - Я не уверен, к чему это относится? Библиотека должна обрабатывать пустые метки (как цитируемые, так и не цитируемые). Он также может читать CSV без строки заголовка / столбца, но тогда он требует, чтобы пользователь указывал это (идентификатор заголовка столбца -1 и идентификатор заголовка строки -1). Пожалуйста, предоставьте более подробную информацию или сообщите об ошибке на странице GitHub, если у вас есть конкретный прецедент, который вы хотите увидеть. Благодаря! – d99kris 24 July 2017 в 14:10

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

std::vector<std::string> vec;
using namespace boost;
tokenizer<escaped_list_separator<char> > tk(
   line, escaped_list_separator<char>('\\', ',', '\"'));
for (tokenizer<escaped_list_separator<char> >::iterator i(tk.begin());
   i!=tk.end();++i) 
{
   vec.push_back(*i);
}
42
ответ дан dtw 18 August 2018 в 05:55
поделиться
  • 1
    Токсикатор повышения не полностью поддерживает полный стандарт CSV, но есть некоторые быстрые обходные пути. См. stackoverflow.com/questions/1120140/csv-parser-in-c/… – Rolf Kristensen 14 April 2010 в 00:03
  • 2
    У вас должна быть полная библиотека ускорений на вашем компьютере или вы можете просто использовать подмножество своего кода для этого? 256mb похоже на многоборье для CSV. – NPike 28 April 2011 в 00:28
  • 3
    @NPike: вы можете использовать утилиту bcp , которая поставляется с boost, чтобы извлекать только те заголовки, которые вам действительно нужны. – ildjarn 25 May 2011 в 00:06

Первое, что вам нужно сделать, это убедиться, что файл существует. Для этого вам просто нужно попытаться открыть поток файлов на пути. После того, как вы открыли поток файлов, используйте 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;
    }
}
3
ответ дан Elizabeth Card 18 August 2018 в 05:55
поделиться

Для чего это стоит, вот моя реализация. Он имеет дело с вводом wstring, но может быть легко скорректирован. Он не обрабатывает новую строку в полях (так как мое приложение тоже не работает, но добавление его поддержки не слишком сложно), и оно не соответствует концу строки «\r\n» в соответствии с RFC (если вы используете std :: getline), но он корректно обрабатывает пробелы и двойные кавычки (надеюсь).

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<unsigned, unsigned> nextCSVQuotePair(const wstring& line, const wchar_t quotChar, unsigned ofs = 0)
{
    pair<unsigned, unsigned> 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<wstring>& 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<unsigned, unsigned> 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();
}
1
ответ дан Fabien 18 August 2018 в 05:55
поделиться

Не стоит излишне использовать 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 представлены где-то посередине и затрагивают различные методы использования Духа. Сгенерированный код такой же жесткий, как ручной код. Проверьте сборщик!

28
ответ дан foraidt 18 August 2018 в 05:55
поделиться
  • 1
    На самом деле это слишком много, время компиляции огромно и делает использование «Духа» для простых «задач микроразборки». неразумным. – Gerdiner 2 December 2012 в 02:37
  • 2
    Также я хотел бы указать, что приведенный выше код не анализирует CSV, он просто анализирует диапазон типа вектора, разделенного запятыми. Он не обрабатывает кавычки, различные типы столбцов и т. Д. Короче говоря, 19 голосов за что-то, что действительно отвечает на вопрос, кажутся мне немного подозрительными. – Gerdiner 2 December 2012 в 02:40
  • 3
    @ Гердиннер ерунда. Время компиляции для небольших парсеров не так уж и велико, но это также не имеет значения, потому что вы вводите код в свой собственный блок компиляции и компилируете его once . Тогда вам нужно только связать его, и это так же эффективно, как и получается. А что касается вашего другого комментария, то есть столько диалектов CSV, сколько есть процессоров для него. Этот, конечно, не очень полезный диалект, но его можно тривиально расширять для обработки цитируемых значений. – Konrad Rudolph 6 December 2012 в 14:04
  • 4
    @konrad: просто включает & quot; #include & lt; boost / spirit / include / qi.hpp & gt; & quot; в пустом файле с основным, и ничего больше не занимает 9.7sec с MSVC 2012 на Corei7 работает на 2.ghz. Это бесполезное раздувание. Принятый ответ компилируется менее чем за 2 секунды на той же машине, мне бы не хотелось представить, как долго будет использоваться «правильный» пример Boost.Spirit для компиляции. – Gerdiner 11 January 2013 в 02:31
  • 5
    @Gerdiner Я должен согласиться с вами накладными расходами на использование духа для чего-то простого, так как обработка cvs слишком велика. – user 25 February 2014 в 02:48

Еще одну библиотеку ввода / вывода 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
  }
}
5
ответ дан Heygard Flisch 18 August 2018 в 05:55
поделиться
  • 1
    Приятно, но это заставляет вас выбирать количество столбцов во время компиляции. Не очень полезно для многих приложений. – quant_dev 26 December 2015 в 21:30

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

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

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<int>(j) << "\t";
      }
      std::cout << std::endl;
   }
   std::cout << std::endl;
}

Можно найти другие примеры

31
ответ дан J Mkdjion 18 August 2018 в 05:55
поделиться
  • 1
    Хотя strtk поддерживает doublequoted fields и даже снимает окружающие кавычки (через options.trim_dquotes = true), он не поддерживает удаление двойных двойных букв (например, поле "She said ""oh no"", and left." как c-строка "She said \"oh no\", and left."), , Тебе придется это сделать самому. – rampion 28 August 2017 в 20:29
  • 2
    При использовании strtk вам также придется вручную обрабатывать двойные кавычки, содержащие символы новой строки. – rampion 29 August 2017 в 19:02

Вы должны гордиться, когда используете что-то настолько прекрасное, как boost::spirit

. Здесь моя попытка парсера (почти), соответствующая спецификациям CSV по этой ссылке спецификациям CSV (Мне не нужны разрывы строк в полях, а также пробелы в запятых).

После того, как вы преодолеете шокирующий опыт ожидания 10 секунд для компиляции этого кода :), вы можете сидеть сложа руки и

// csvparser.cpp
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include <iostream>
#include <string>

namespace qi = boost::spirit::qi;
namespace bascii = boost::spirit::ascii;

template <typename Iterator>
struct csv_parser : qi::grammar<Iterator, std::vector<std::string>(), 
    bascii::space_type>
{
    qi::rule<Iterator, char()                                           > COMMA;
    qi::rule<Iterator, char()                                           > DDQUOTE;
    qi::rule<Iterator, std::string(),               bascii::space_type  > non_escaped;
    qi::rule<Iterator, std::string(),               bascii::space_type  > escaped;
    qi::rule<Iterator, std::string(),               bascii::space_type  > field;
    qi::rule<Iterator, std::vector<std::string>(),  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<iterator_type> csv_parser;

    csv_parser grammar;
    std::string str;
    int fid;
    while (getline(std::cin, str))
    {
        fid = 0;

        if (str.empty())
            break;

        std::vector<std::string> 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;
}

Компиляция:

make csvparser

Тест (пример украден из 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
2
ответ дан jav 18 August 2018 в 05:55
поделиться

Вот код для чтения матрицы, обратите внимание, что у вас также есть функция csvwrite в matlab

void loadFromCSV( const std::string& filename )
{
    std::ifstream       file( filename.c_str() );
    std::vector< std::vector<std::string> >   matrix;
    std::vector<std::string>   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<int(matrix.size()); i++ )
    {
        for( int j=0; j<int(matrix[i].size()); j++ )
            std::cout << matrix[i][j] << " ";

        std::cout << std::endl;
    }
}
2
ответ дан Jim M. 18 August 2018 в 05:55
поделиться

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

В качестве примера того, как я хотел использовать входной поток CSV, рассмотрим следующий ввод (взятый из страница wikipedia на 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 <typename T, bool> struct set_value {
        void operator () (std::string in, T &v) const {
            std::istringstream(in) >> v;
        }
    };
    template <typename T> struct set_value<T, true> {
        template <bool SIGNED> 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<is_signed_int<T>::val>(in, v);
        }
    };
    template <typename T> const csv_istream & operator >> (T &v) const {
        std::string tmp;
        scan(&tmp);
        set_value<T, is_int<T>::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 <typename T>
    const csv_istream & operator >> (T &(*manip)(T &)) const {
        is_ >> manip;
        return *this;
    }
    operator bool () const { return !is_.fail(); }
};

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

template <typename T> struct is_signed_int { enum { val = false }; };
template <> struct is_signed_int<short> { enum { val = true}; };
template <> struct is_signed_int<int> { enum { val = true}; };
template <> struct is_signed_int<long> { enum { val = true}; };
template <> struct is_signed_int<long long> { enum { val = true}; };

template <typename T> struct is_unsigned_int { enum { val = false }; };
template <> struct is_unsigned_int<unsigned short> { enum { val = true}; };
template <> struct is_unsigned_int<unsigned int> { enum { val = true}; };
template <> struct is_unsigned_int<unsigned long> { enum { val = true}; };
template <> struct is_unsigned_int<unsigned long long> { enum { val = true}; };

template <typename T> struct is_int {
    enum { val = (is_signed_int<T>::val || is_unsigned_int<T>::val) };
};
5
ответ дан jxh 18 August 2018 в 05:55
поделиться

Я написал только компилятор C ++ 11 CSV . Он хорошо протестирован, быстро, поддерживает всю спецификацию CSV (цитируемые поля, разделитель / терминатор в кавычках, экранирование прокрутки и т. Д.) И настраивается для учета CSV, которые не соответствуют спецификации.

Конфигурация выполняется через свободный интерфейс:

// 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

Анализ - это всего лишь диапазон, основанный на цикле:

#include <iostream>
#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;
  }
}
3
ответ дан m0meni 18 August 2018 в 05:55
поделиться
  • 1
    Хорошая работа, но вам нужно добавить еще три вещи: (1) прочитать заголовок (2) предоставить индексирование полей по имени (3) не перераспределять память в цикле, повторно используя один и тот же вектор строк – Maksym Ganenko 23 July 2017 в 10:14
  • 2
    @ Максим Ганенко, я делаю # 3. Не могли бы вы подробнее остановиться на №2? – m0meni 23 July 2017 в 18:48
  • 3
    Очень полезно получать поля не по положению в строке, а по имени, указанному в заголовке (в первой строке таблицы CSV). Например, я ожидаю, что таблица CSV с «Date» поле, но я не знаю, что такое «Дата». индекс поля в строке. – Maksym Ganenko 24 July 2017 в 07:50
  • 4
    @MaksymGanenko ах, я понимаю, что вы имеете в виду. Там github.com/ben-strasser/fast-cpp-csv-parser , когда вы знаете столбцы своего CSV во время компиляции, и, вероятно, это лучше, чем у меня. То, что я хотел, было парсером CSV для случаев, когда вы хотели использовать один и тот же код для разных CSV и не знаете, как они выглядят раньше времени. Поэтому я, вероятно, не добавлю # 2, но я буду добавлять # 1 в будущем. – m0meni 24 July 2017 в 16:12

Это старый поток, но он все еще находится в верхней части результатов поиска, поэтому я добавляю свое решение, используя std :: stringstream и простой метод замены строки Yves Baumes, который я нашел здесь.

Следующий пример будет читать файл по строкам, игнорировать строки комментариев, начинающиеся с //, и анализировать другие строки в комбинации строк, ints и double. Stringstream выполняет разбор, но ожидает, что поля будут разделены пробелами, поэтому я использую stringreplace, чтобы сначала включить запятые в пространства.

Плохой или отсутствующий ввод просто игнорируется, что может быть или не быть хорошим, в зависимости от ваших обстоятельств.

#include <string>
#include <sstream>
#include <fstream>

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));
      }
   }
}
1
ответ дан marcp 18 August 2018 в 05:55
поделиться

Еще один быстрый и простой способ - использовать Boost.Fusion I/O :

#include <iostream>
#include <sstream>

#include <boost/fusion/adapted/boost_tuple.hpp>
#include <boost/fusion/sequence/io.hpp>

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<CsvString, int, bool, double> 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)
1
ответ дан Maxim Egorushkin 18 August 2018 в 05:55
поделиться

Так как я не привык к прямо сейчас, я предлагаю более простое решение. Предположим, что ваш CSV-файл имеет 100 строк с 10 номерами в каждой строке, разделенных символом ','. Вы можете загрузить эти данные в виде массива со следующим кодом:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
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;}
        }
    }


}
1
ответ дан nikos_k 18 August 2018 в 05:55
поделиться

Если вы не хотите иметь дело с включением повышения в свой проект (это значительно больше, если все, что вы собираетесь использовать для него, это разбор CSV ...)

Мне повезло с синтаксический анализ CSV здесь:

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

Он обрабатывает цитируемые поля - но не обрабатывает встроенные\n символы (что, вероятно, отлично подходит для большинства применений).

1
ответ дан NPike 18 August 2018 в 05:55
поделиться
  • 1
    Разве компилятор не выделяет все, что не имеет существенного значения? – tofutim 12 July 2012 в 06:46

Это решение обнаруживает эти 4 случая

. Полный класс находится в

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,",

Он читает символ файла по символу и читает по 1 строке за один раз вектору (строк), поэтому подходит для очень больших файлов.

Использование

Итерация до тех пор, пока не будет возвращена пустая строка (конец файла). Строка представляет собой вектор, в котором каждая запись представляет собой столбец CSV.

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

объявление класса

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

реализация

std::vector<std::string> read_csv_t::read_row()
{
  bool quote_mode = false;
  std::vector<std::string> 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<std::string> v;
  return v;
}
2
ответ дан Pedro Vicente 18 August 2018 в 05:55
поделиться

Извините, но все это похоже на сложный синтаксис, чтобы скрыть несколько строк кода.

Почему бы не это:

/**

  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<std::string>& 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,q-p-1) );
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<std::string> 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;
}
2
ответ дан ravenspoint 18 August 2018 в 05:55
поделиться
  • 1
    Erm, почему в строке будет ",\n"? – Timmmm 18 November 2014 в 21:36
  • 2
    @Timmmm ищет метод substr класса String, и вы увидите, что он принимает несколько символов,\n - символ новой строки, поэтому в этом случае он считается единственным символом. Он не ищет всю ценность в целом. Он ищет каждого персонажа; а именно запятой или новой линией. substr вернет позицию первого найденного символа и -1, если не найдет ни одного, что означает, что он закончил чтение строки. fp отслеживает позицию в файле внутри, поэтому каждый вызов readCSV перемещает его по одной строке за раз. – Martyn Shutt 5 June 2015 в 19:23

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

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

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

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

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

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

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

вокруг, чтобы исправить поведение по умолчанию для повышения e scaped_list_separator:

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

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

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

14
ответ дан Rolf Kristensen 18 August 2018 в 05:55
поделиться

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

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

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

#include <istream>
#include <string>
#include <vector>

enum class CSVState {
    UnquotedField,
    QuotedField,
    QuotedQuote
};

std::vector<std::string> readCSVRow(const std::string &row) {
    CSVState state = CSVState::UnquotedField;
    std::vector<std::string> 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<std::vector<std::string>> readCSV(std::istream &in) {
    std::vector<std::vector<std::string>> 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;
}
28
ответ дан sastanin 18 August 2018 в 05:55
поделиться
  • 1
    спасибо, я думаю, что это самый полный ответ, слишком плохо, что он похоронен здесь. – mihai 24 June 2016 в 12:36
  • 2
    этот вложенный вектор строк не подходит для современных процессоров. Отбрасывает способность кеширования – Nikolaos Giotis 5 April 2018 в 06:56
  • 3
    плюс вы получили все эти инструкции switch – Nikolaos Giotis 5 April 2018 в 07:05
  • 4
    Главный ответ не работал для меня, так как я на старшем компиляторе. Этот ответ сработал, векторная инициализация может потребовать этого: const char *vinit[] = {""}; vector<string> fields(vinit, end(vinit)); – dr_rk 6 April 2018 в 09:16

Я написал хороший способ разбора CSV-файлов, и я подумал, что должен добавить его в качестве ответа:

#include <algorithm>
#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>

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<std::string> stringSplit( const std::string &str, const std::string &delim )
{
  std::vector<std::string> 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;
}
1
ответ дан scap3y 18 August 2018 в 05:55
поделиться

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

escaped_list_separator анализирует надмножество csv. Boost :: tokenizer

Используется только заголовочные файлы Boost tokenizer, не требующие связывания с требуемыми библиотеками.

Вот пример, см. Parse CSV File With Boost Tokenizer В C ++ для деталей или Boost::tokenizer):

#include <iostream>     // cout, endl
#include <fstream>      // fstream
#include <vector>
#include <string>
#include <algorithm>    // copy
#include <iterator>     // ostream_operator
#include <boost/tokenizer.hpp>

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<char> > 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<string>(cout, "|"));

        cout << "\n----------------------" << endl;
    }
}
28
ответ дан stefanB 18 August 2018 в 05:55
поделиться
  • 1
    И если вы хотите анализировать встроенные новые строки mybyteofcode.blogspot.com/2010/11/… . – stefanB 12 January 2011 в 23:35
  • 2
    Хотя этот метод работает, я обнаружил, что он имеет очень низкую производительность. Разбор CSV-файла 90000 строк с десятью полями в строке занимает около 8 секунд на моем 2 ГГц Xeon. Модуль csv стандартной библиотеки Python анализирует один и тот же файл примерно за 0,3 секунды. – Rob Smallshire 27 June 2012 в 08:59
  • 3
    @Rob это интересно - что делает Python csv по-другому? – tofutim 12 July 2012 в 06:48
  • 4
    @RobSmallshire это простой пример кода, а не высокопроизводительный. Этот код делает копии всех полей в строке. Для повышения производительности вы будете использовать разные опции и возвращать только ссылки на поля в буфере вместо создания копий. – stefanB 16 July 2012 в 01:43

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

 void ParseCSV(const string& csvSource, vector<vector<string> >& lines)
    {
       bool inQuote(false);
       bool newLine(false);
       string field;
       lines.clear();
       vector<string> 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);
    }
17
ответ дан unixman83 18 August 2018 в 05:55
поделиться

Вы также можете взглянуть на возможности библиотеки Qt.

Он поддерживает регулярные выражения, а класс QString имеет хорошие методы, например. split() возвращает QStringList, список строк, полученных путем разделения исходной строки с предоставленным разделителем. Должно быть достаточно для файла csv ..

Чтобы получить столбец с заданным именем заголовка, я использую следующее: c ++ наследование Qt проблема qstring

5
ответ дан Community 18 August 2018 в 05:55
поделиться
  • 1
    это не будет обрабатывать запятые в кавычках – Ezee 26 March 2015 в 07:41
  • 2
    Вы можете не заботиться о встраивании, потому что большинство компиляторов сами это решают. По крайней мере, я уверен в Visual C ++. Он может встроить метод независимо от вашей спецификации метода. – MrPisarik 29 January 2016 в 16:56
  • 3
    Именно поэтому я их четко обозначил. Gcc и Clang, которые я использую в основном, имеют свои собственные соглашения. A "inline" ключевое слово должно быть просто стимулом. – Pietro Saccardi 29 January 2016 в 16:59
5
ответ дан Community 6 September 2018 в 19:53
поделиться
5
ответ дан Community 6 September 2018 в 19:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: