2008-09-25 3 views
30

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

Мой вопрос в том, что это лучший способ прочитать эту информацию и «импортировать ее» в поток строк? Проблема с этим подходом (см. Ниже) заключается в том, что при создании потока строк буферы копируются, а использование памяти удваивается.

#include <fstream> 
#include <sstream> 

using namespace std; 

int main() { 
    ifstream is; 
    is.open (sFilename.c_str(), ios::binary); 

    // get length of file: 
    is.seekg (0, std::ios::end); 
    long length = is.tellg(); 
    is.seekg (0, std::ios::beg); 

    // allocate memory: 
    char *buffer = new char [length]; 

    // read data as a block: 
    is.read (buffer,length); 

    // create string stream of memory contents 
    // NOTE: this ends up copying the buffer!!! 
    istringstream iss(string(buffer)); 

    // delete temporary buffer 
    delete [] buffer; 

    // close filestream 
    is.close(); 

    /* ================================== 
    * Use iss to access data 
    */ 

} 
+2

Вам нравится копировать данные. 1) Скопировать в буфер. 2) Скопировать в анонимную std :: string. 3) Скопировать в iss. – 2008-09-26 10:34:43

+0

Возможно, вам следует искать в памяти сопоставленные файлы. – 2008-09-25 09:50:28

ответ

32

std::ifstream имеет метод rdbuf(), который возвращает указатель на filebuf. Затем вы можете «толкать» это filebuf в ваш stringstream:

int main() 
{ 
    std::ifstream file("myFile"); 

    if (file) 
    { 
     std::stringstream buffer; 

     buffer << file.rdbuf(); 

     file.close(); 

     // operations on the buffer... 
    } 
} 

EDIT: Как Мартин-Йорк замечания в комментариях, это может быть не самым быстрым решением, поскольку stringstream «s operator<< прочтет filebuf посимвольно. Возможно, вы захотите проверить его ответ, где он использует метод read, как вы это делали, а затем установите буфер stringstream, чтобы указать на ранее выделенную память.

+0

Hi Luc, Я согласился с вашим предложением ... манипулирование rdbuf - это путь! Но у вашего решения нет такой же проблемы? Разве вы не создаете 2 копии одного и того же буфера, хотя бы на мгновение? – 2008-09-25 10:18:57

1

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

0

Еще одна вещь, о которой следует помнить, это то, что файловый ввод-вывод всегда будет самой медленной операцией. Решение Luc Touraille верное, но есть и другие варианты. Чтение всего файла в память сразу будет намного быстрее, чем отдельные чтения.

40

OK. Я не говорю, что это будет быстрее, чем чтение из файла

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

N.B. Стоит отметить, что std :: ifstream буферизуется. Он считывает данные из файла в (относительно больших) фрагментах. Операции потока выполняются против буфера, только возвращающегося к файлу для другого, когда требуется больше данных. Поэтому, прежде чем всасывать все данные в память, убедитесь, что это бутылочная горловина.

#include <fstream> 
#include <sstream> 
#include <vector> 

int main() 
{ 
    std::ifstream  file("Plop"); 
    if (file) 
    { 
     /* 
     * Get the size of the file 
     */ 
     file.seekg(0,std::ios::end); 
     std::streampos   length = file.tellg(); 
     file.seekg(0,std::ios::beg); 

     /* 
     * Use a vector as the buffer. 
     * It is exception safe and will be tidied up correctly. 
     * This constructor creates a buffer of the correct length. 
     * 
     * Then read the whole file into the buffer. 
     */ 
     std::vector<char>  buffer(length); 
     file.read(&buffer[0],length); 

     /* 
     * Create your string stream. 
     * Get the stringbuffer from the stream and set the vector as it source. 
     */ 
     std::stringstream  localStream; 
     localStream.rdbuf()->pubsetbuf(&buffer[0],length); 

     /* 
     * Note the buffer is NOT copied, if it goes out of scope 
     * the stream will be reading from released memory. 
     */ 
    } 
}