Вопрос по c++ – Быстрый и простой анализ CSV в C ++

8

Я пытаюсь проанализировать простой файл CSV с данными в таком формате, как:

20.5,20.5,20.5,0.794145,4.05286,0.792519,1
20.5,30.5,20.5,0.753669,3.91888,0.749897,1
20.5,40.5,20.5,0.701055,3.80348,0.695326,1

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

string field;
getline(file,line);
stringstream ssline(line);

getline( ssline, field, ',' );
stringstream fs1(field);
fs1 >> cent_x.at(n);

getline( ssline, field, ',' );
stringstream fs2(field);
fs2 >> cent_y.at(n);

getline( ssline, field, ',' );
stringstream fs3(field);
fs3 >> cent_z.at(n);

getline( ssline, field, ',' );
stringstream fs4(field);
fs4 >> u.at(n);

getline( ssline, field, ',' );
stringstream fs5(field);
fs5 >> v.at(n);

getline( ssline, field, ',' );
stringstream fs6(field);
fs6 >> w.at(n);

Проблема в том, что это очень медленно (более одного миллиона строк на файл данных), и мне кажется, что это немного не элегантно. Есть ли более быстрый подход с использованием стандартной библиотеки, или я должен просто использовать функции stdio? Мне кажется, весь этот блок кода сводится к одному вызову fscanf.

Заранее спасибо!

Дубликат следующего вопроса:stackoverflow.com/questions/1120140/csv-parser-in-c Zamfir Kerlukson

Ваш Ответ

3   ответа
2

что основным узким местом (за исключением небуферизованного ввода-вывода на основе getline ()) является разбор строк. Поскольку у вас есть & quot;, & quot; В качестве разделителя вы можете выполнить линейное сканирование строки и заменить все "," \ quot; \ 0 & quot; (маркер конца строки, нулевой терминатор).

Что-то вроде этого:

// tmp array for the line part values
double parts[MAX_PARTS];

while(getline(file, line))
{
    size_t len = line.length();
    size_t j;

    if(line.empty()) { continue; }

    const char* last_start = &line[0];
    int num_parts = 0;

    while(j < len)
    {
        if(line[j] == ',')
        {
           line[j] = '\0';

           if(num_parts == MAX_PARTS) { break; }

           parts[num_parts] = atof(last_start);
           j++;
           num_parts++;
           last_start = &line[j];
        }
        j++;
    }

    /// do whatever you need with the parts[] array
 }
1

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

Затем вы можете сделать что-то вроде этого для разбора вашей строки:

//Delimited string to vector
vector<string> dstov(string& str, string delimiter)
{
  //Vector to populate
  vector<string> ret;
  //Current position in str
  size_t pos = 0;
  //While the the string from point pos contains the delimiter
  while(str.substr(pos).find(delimiter) != string::npos)
  {
    //Insert the substring from pos to the start of the found delimiter to the vector
    ret.push_back(str.substr(pos, str.substr(pos).find(delimiter)));
    //Move the pos past this found section and the found delimiter so the search can continue
    pos += str.substr(pos).find(delimiter) + delimiter.size();
  }
  //Push back the final element in str when str contains no more delimiters
  ret.push_back(str.substr(pos));
  return ret;
}

string rawfiledata;

//This call will parse the raw data into a vector containing lines of
//20.5,30.5,20.5,0.753669,3.91888,0.749897,1 by treating the newline
//as the delimiter
vector<string> lines = dstov(rawfiledata, "\n");

//You can then iterate over the lines and parse them into variables and do whatever you need with them.
for(size_t itr = 0; itr < lines.size(); ++itr)
  vector<string> line_variables = dstov(lines[itr], ",");
8

когда вы можете сделать это только с одним уверенным, не поможет в этом. спектакль. Попробуйте это вместо:

string line;
getline(file, line);

istringstream ss(line);  // note we use istringstream, we don't need the o part of stringstream

char c1, c2, c3, c4, c5;  // to eat the commas

ss >> cent_x.at(n) >> c1 >>
      cent_y.at(n) >> c2 >>
      cent_z.at(n) >> c3 >>
      u.at(n) >> c4 >>
      v.at(n) >> c5 >>
      w.at(n);

Если вы знаете количество строк в файле, вы можете изменить размеры векторов перед чтением, а затем использоватьoperator[] вместоat(), Таким образом, вы избегаете проверки границ и, таким образом, получаете небольшую производительность.

Error: User Rate Limit Exceeded Kyle Lynch
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededcharError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

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