如何将文件内容读入istringstream?

Mar*_*nto 37 c++ memory optimization stream stringstream

为了提高从文件读取的性能,我试图将大(几MB)文件的整个内容读入内存,然后使用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
   */

}
Run Code Online (Sandbox Code Playgroud)

Luc*_*lle 42

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...
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:由于马丁在纽约的言论答记者问,这可能不是最快的解决方案,因为stringstreamoperator<<将字符读filebuf字符.你可能想检查他的答案,他在那里使用你以前ifstreamread方法,然后将stringstream缓冲区设置为指向先前分配的内存.

  • 因为运算符<<()看到rdbuf()的结果时它只是一个流缓冲区,此时没有文件缓冲区的概念,它无法查找其长度,因此必须使用循环来读取1个字符一次.字符串流内部缓冲区(std :: string)也必须调整为插入的数据大小. (6认同)
  • 它似乎正在删除我需要的换行符。 (2认同)

Mar*_*ork 41

好.我并不是说这比阅读文件更快

但是这是一种创建缓冲区的方法,在将数据读入缓冲区之后,将其直接用作stringstream的源.

NB值得一提的是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.
         */
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我不认为有关"因为char是POD数据类型它没有被初始化".是正确的.构造函数实际上有两个参数,第二个是用于初始化元素的值.在我们的例子中,它默认为`T()`或`char()`,意思是0.所以所有元素都应该是0. (5认同)
  • @Martin York,您如何了解这些细节,在您遇到问题时阅读或研究,并反过来了解所有这些细节?非常感谢,bdw. (3认同)
  • @Gollum:不,这只是从两个方面获得的细节.1)始终使用流类.2)实现了我自己的流类.Number(2)使您可以阅读有关流应该如何工作的大量内容,因为您希望它以与流标准流相同的方式工作(以便您可以重用STL库)标准流的功能).上面唯一的非intatve位是修改流缓冲区的工作方式. (3认同)
  • -1,此方法(basic_stringbuf :: setbuf)是实现定义的. (3认同)
  • @ybungalobill:是的.实现定义不是'未定义' (2认同)
  • @Martin:你是对的,它不是"未定义的行为",但它不可移植,所以我不能称它为"标准C++解决方案". (2认同)