2010-06-24 3 views
2

Обычно я использую метод, описанный в csv parser, для чтения файлов электронных таблиц. Однако при чтении 64-мегабайтного файла, который содержит около 40 столбцов и 250 тыс. Строк данных, это занимает около 4 минут. В исходном методе класс CSVRow используется для чтения файла по строкам, а частный вектор используется для хранения всех данных в строке.эффективно читает файл больших таблиц в C++

Несколько вещей отметить:

  • я сделал резервную достаточную емкость вектора, но не очень полезно.
  • Мне также нужно создавать экземпляры некоторого класса при чтении каждой строки, но даже когда код просто читается в данных без создания каких-либо экземпляров, это занимает много времени.
  • Файл разделен на табу вместо запятой, но я не думаю, что это имеет значение.

Поскольку некоторые столбцы в этом файле не являются полезными данными, я изменил метод, чтобы иметь частный член строки для хранения всех данных, а затем найти позицию (n-1) -го и n-го разделителей для получить полезные данные (конечно, есть много полезных столбцов). Поступая таким образом, я избегаю некоторых операций push_back и сокращаю время до чуть более 2 минут. Однако это все еще кажется слишком длинным для меня.

Вот мои вопросы:

  1. Есть ли способ, чтобы более эффективно прочитать такой файл с таблиц?

  2. Должен ли я считать файл в буфере вместо строки за строкой? Если да, то как читать по буфере и использовать класс csvrow ?

  3. Я не пробовал повышать токенизатор, это , что более эффективно?

Благодарим за помощь!

+1

Тяжелый куча хлеба. Читайте это: http://blogs.msdn.com/b/ricom/archive/2005/05/10/performance-quiz-6-chinese-english-dictionary-reader.aspx?wa=wsignin1.0 –

ответ

2

Похоже, что вы являетесь бутылочным горлом от IO. Вместо того, чтобы читать файл по строкам, прочитайте его в блоках, возможно, 8 МБ. Разберите блок, прочитанный для записей, и определите, является ли конец блока частичной записью. Если это так, скопируйте часть последней записи из блока и добавьте ее в следующий блок. Повторяйте, пока файл не будет прочитан. Таким образом, для 64-мегабайтного файла вы делаете только 8 запросов ввода-вывода. Вы можете поэкспериментировать с размером блока, чтобы определить, что дает лучшую производительность и использование памяти.

0

Если чтение целых данных в память приемлемой (и, видимо, есть), то я бы это сделать:

  1. Читать весь файл в станд :: вектор
  2. заселить вектор>, который содержит начальные позиции всех символов новой строки и ячейки данных. Эти позиции обозначают начало/конец каждой ячейки

Часть кода эскиз, чтобы продемонстрировать идею:

vector<vector<vector<char>::size_Type> > rows; 
for (vector<char>::size_type i = 0; i < data.size(); ++i) { 
    vector<vector<char>::size_type> currentRow; 
    currentRow.push_back(i); 
    while (data[i] != '\n') { 
     if (data[i] == ',') { // XXX consider comma at end of line 
      currentRow.push_back(i); 
     } 
    } 
    rows.push_back(currentRow); 
} 
// XXX consider files which don't end in a newline 

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

// XXX error checking omitted for simplicity 
string getCellText(int row, int col) 
{ 
    // XXX Needs handling for last cell of a line 
    const vector<char>::size_type start = rows[row][col]; 
    const vector<char>::size_type end = rows[row][col + 1]; 
    return string(data[start], data[end]); 
} 
0

This article должен быть полезным.

Вкратце:
1. Либо использовать файлы с отображением памяти, либо читать файл в блоках 4kbyte для доступа к данным. Файлы с отображением памяти будут быстрее.
2. Старайтесь избегать использования push_back, std :: string операций (например, +) и подобных процедур из stl в цикле синтаксического анализа. Они хороши, но они ВСЕ используют динамически выделенную память, а динамическое распределение памяти происходит медленно. Все, что часто динамически распределяется, сделает вашу программу медленнее. Попытайтесь предварительно распределить все буферы перед разбором. Подсчет всех токенов для предопределения памяти для них не должен быть трудным.
3. Используйте профайлер, чтобы определить, что вызывает замедление.
4. Возможно, вы захотите избежать использования операторов iostream < < и >> и проанализировать файл самостоятельно.

В целом, эффективная реализация парсера C/C++ должна анализировать 20 мегабайт большого текстового файла в течение 3 секунд.

Смежные вопросы