10

我想将文件读入字符串。我正在寻找不同的方法来有效地做到这一点。

使用固定大小的 *char 缓冲区

我收到了Tony 的回答,它创建了一个 16 kb 缓冲区并读入该缓冲区并附加缓冲区,直到没有更多内容可读取。我了解它是如何工作的,而且我发现它非常快。我不明白的是,在该答案的评论中,据说这种方式将所有内容复制了两次。但据我了解,它只发生在内存中,而不是磁盘中,所以几乎不引人注意。它从缓冲区复制到内存中的字符串是否有问题?

使用 istreambuf_iterator

我收到的另一个答案使用 istreambuf_iterator。代码看起来很漂亮而且很小,但是速度非常慢。我不知道为什么会这样。为什么那些迭代器这么慢?

使用 memcpy()

对于这个问题,我收到了我应该使用 memcpy() 的评论,因为它是最快的本机方法。但是如何将 memcpy() 与字符串和 ifstream 对象一起使用?ifstream 不应该使用自己的读取功能吗?为什么使用 memcpy() 会破坏可移植性?我正在寻找与 VS2010 以及 GCC 兼容的解决方案。为什么 memcpy() 不能与这些一起使用?

+ 还有其他有效的方法吗?

对于小于 10 MB 的小二进制文件,您推荐什么,我使用什么外壳?

(我不想将这个问题分成几部分,因为我对如何将 ifstream 读入字符串的不同方式之间的比较更感兴趣)

4

2 回答 2

10

它只发生在内存中,而不是磁盘中,因此几乎不引人注意

这确实是正确的。尽管如此,不这样做的解决方案可能会更快。

为什么那些迭代器这么慢?

代码很慢不是因为迭代器,而是因为字符串不知道要分配多少内存:istreambuf_iterators 只能遍历一次,因此字符串基本上被迫执行重复连接并产生内存重新分配,这非常慢。

我最喜欢的单线,从另一个答案是直接从底层缓冲区流式传输:

string str(static_cast<stringstream const&>(stringstream() << in.rdbuf()).str());

在最近的平台上,这确实会预先分配缓冲区。但是,它仍然会导致冗余副本(从stringstream到最终字符串)。

于 2011-06-28T18:13:06.650 回答
4

最通用的方法可能是使用以下响应 istreambuf_iterator

std::string s( (std::istreambuf_iterator<char>( source )),
               (std::istreambuf_iterator<char>()) );

尽管确切的性能非常依赖于实现,但这不太可能是最快的解决方案。

一个有趣的替代方案是:

std::istringstream tmp;
tmp << source.rdbuf();
std::string s( tmp.str() );

这可能非常快,如果实现对operator<<您正在使用的 . 文件以及它如何在 istringstream. 然而,一些早期的实现(可能还有最近的实现)在这方面非常糟糕。

一般来说,使用 an 的性能std::string将取决于实现在增长字符串时的效率。实现无法确定最初的规模。您可能想比较使用相同代码的第一个算法,std::vector<char> 而不是std::string,或者如果您可以很好地估计最大大小,使用reserve,或类似的东西:

std::string s( expectedSize, '\0' );
std::copy( std::istreambuf_iterator<char>( source ),
           std::istreambuf_iterator<char>(),
           s.begin() );

memcpy无法从文件中读取,并且使用良好的编译器,不会像使用std::copy(使用相同的数据类型)一样快。

我倾向于使用上面的第二种解决方案,使用<<on rdbuf(),但这部分是出于历史原因;istrstream在将 STL 添加到标准库之前,我已经习惯了这样做(使用)。就此而言,您可能想尝试使用 istrstream预先分配的缓冲区(假设您可以找到合适的缓冲区大小)。

于 2011-06-28T18:11:55.287 回答