Как прочитать содержимое файла в istringstream? - PullRequest
36 голосов
/ 25 сентября 2008

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

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

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

}

Ответы [ 4 ]

41 голосов
/ 26 сентября 2008

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

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

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.
         */
    }
}
39 голосов
/ 25 сентября 2008

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

#include <fstream>
#include <sstream>

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

    if ( file )
    {
        std::stringstream buffer;

        buffer << file.rdbuf();

        file.close();

        // operations on the buffer...
    }
}

РЕДАКТИРОВАТЬ: Как отмечает Мартин Йорк в комментариях, это может быть не самое быстрое решение, так как stringstream operator<< будет читать символьный символ filebuf. Возможно, вы захотите проверить его ответ, где он использует ifstream метод read, как вы это делали, а затем установите буфер stringstream, чтобы он указывал на ранее выделенную память.

1 голос
/ 25 сентября 2008

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

0 голосов
/ 25 сентября 2008

Следует также помнить, что файловый ввод-вывод всегда будет самой медленной операцией. Решение Люка Турэля правильное, но есть и другие варианты. Чтение всего файла в память за раз будет намного быстрее, чем отдельное чтение.

...