如何将文件内容读入 istringstream?

Posted

技术标签:

【中文标题】如何将文件内容读入 istringstream?【英文标题】:How to read file content into istringstream? 【发布时间】:2010-09-13 00:57:14 【问题描述】:

为了提高从文件读取的性能,我尝试将一个大(几 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
   */


【问题讨论】:

也许你应该搜索内存映射文件。 要记住的另一件事是文件 I/O 总是最慢的操作。 Luc Touraille 的解决方案是正确的,但还有其他选择。一次将整个文件读入内存将比单独读取快得多。 您喜欢复制数据。 1)复制到缓冲区。 2) 复制到匿名 std::string 中。 3)复制到iss。 【参考方案1】:

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...
    

编辑:正如 Martin York 在 cmets 中所说,这可能不是最快的解决方案,因为 stringstreamoperator&lt;&lt; 将逐个字符地读取 filebuf。您可能想检查他的答案,他使用ifstreamread 方法,就像您以前一样,然后将stringstream 缓冲区设置为指向先前分配的内存。

【讨论】:

嗨 Luc,我同意你的建议...... rdbuf 的操作是要走的路!但是您的解决方案没有同样的问题吗?你不会至少暂时创建同一个缓冲区的 2 个副本吗? 因为到时候 operator 它似乎正在删除我需要的换行符。 是否需要file.close(); @artm 这不是强制性的,但最好在使用完文件句柄后立即关闭它们。如果没有对close 的显式调用,当file 被破坏时(在其范围的末尾,因此在main 的末尾),文件将被关闭。最好的方法可能是限制file 的范围。【参考方案2】:

好的。我并不是说这会比从文件中读取更快

但这是一种创建缓冲区的方法,在将数据读入缓冲区后,直接将其用作字符串流的源。

注意值得一提的是 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.
         */
    

【讨论】:

@Martin York,你是如何学习这些细节的,当你遇到问题时你会阅读或研究,然后你会学习所有这些细节吗?非常感谢,bdw。 @Gollum:不,这只是从两个方面获得的细节。 1)一直使用流类。 2)实现了我自己的流类。数字 (2) 让您大量阅读有关流应该如何工作的内容,因为您希望它对流的工作方式与对标准流的工作方式相同(以便您可以重用 STL 库标准流的功能)。上面唯一不完整的一点是修改流缓冲区的工作方式。 我不认为“因为 char 是一种 POD 数据类型它没有被初始化”。是正确的。构造函数实际上有两个参数,第二个是初始化元素的值。在我们的例子中默认为T()char(),意思是0。所以所有的元素都应该是0。 -1,这个方法(basic_stringbuf::setbuf)是实现定义的。 @ybungalobill:是的。定义的实现不是“未定义”【参考方案3】:

这对我来说似乎是过早的优化。处理过程中完成了多少工作。假设一个现代化的桌面/服务器,而不是嵌入式系统,在初始化期间复制几 MB 的数据相当便宜,尤其是与首先从磁盘读取文件相比。我会坚持你所拥有的,在系统完成时对其进行测量,并决定潜在的性能提升是否值得。当然,如果内存紧张,这是在一个内部循环中,或者是一个经常被调用的程序(比如每秒一次),这会改变平衡。

【讨论】:

以上是关于如何将文件内容读入 istringstream?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 使用 istringstream 将整数读取为无符号字符

如何将文件内容读入批处理文件中的变量?

如何将文件的内容读入C中的字符串?

带有 JSON 格式字符串的文件,如何将文件内容读入 NSDictionary

如何将 csv 文件的内容读入一个类,每个 csv 行作为一个类实例

如何在MATLAB中读入多个音频文件啊?