2011-06-28 93 views
8

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

使用固定大小的*字符缓冲区

我收到了托尼一个answer什么创造一个16 KB缓冲区,并读取到该缓冲区,并附加缓冲,直到有没有更多的阅读。我明白它是如何工作的,我发现它非常快。我不明白的是,在这个答案的评论中说,这种方式复制一切两次。但据我了解,它只发生在记忆中,而不是来自磁盘,所以它几乎不可感知。它是从缓冲区复制到内存中的字符串的问题吗?

使用istreambuf_iterator

我收到的other answer使用istreambuf_iterator。代码看起来很漂亮,很小,但速度非常慢。我不知道为什么会发生。为什么这些迭代器如此缓慢?

使用的memcpy()

对于this question我收到了,我应该使用memcpy(),因为它是最快的本地方法的意见。但是,我怎样才能使用memcpy()与一个字符串和一个ifstream对象? ifstream是否应该使用自己的读取函数?为什么使用memcpy()破坏可移植性?我正在寻找一个与VS2010以及GCC兼容的解决方案。为什么memcpy()不能与这些工作?

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

你有什么建议,我使用什么shell,对于小的< 10 MB的二进制文件?

(我不想拆零件这个问题,因为我更感兴趣的是不同方式之间的比较,我怎么能读一个ifstream的成字符串)

+0

memcpy()注释是指使用内存映射文件进行读取,而不是使用istream进行读取。内存映射文件不可移植,因为它依赖于OS API。 – Dikei

+0

当你测量性能时,你是在释放还是调试模式下进行测试?你有优化吗?你有关闭迭代器检查吗?默认情况下,visual studio有额外的标准迭代器检查,可能会损害性能。 – luke

+0

[如何预先为std :: string对象分配内存]的可能重复(http://stackoverflow.com/questions/3303527/how-to-pre-allocate-memory-for-a-stdstring-object/ 3304059#3304059)?也许是我看过的最精确的重复。整个第一句话实际上是相同的(唯一的区别是“我需要......”与“我想...”) –

回答

7

它只发生在内存中,不从磁盘,所以它几乎是容易被忽视的

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

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

的代码是慢,因为迭代器,而是因为该字符串不知道多少内存来分配:在istreambuf_iterator s只能进行一次穿越因此字符串基本上是被迫,导致内存中执行重复串连重新分配,这是非常缓慢的。

我最喜欢的一个内胆,从another answer直接从底层缓冲流:

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

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

+1

我只是计时不同的解决方案,你的速度比所有基于迭代器的速度快大约8倍。非常好的一个。 –

3

最普遍的方法是可能是使用 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。然而,一些早期的实现(也许最近的一些也是如此)在这方面非常糟糕。

一般来说,使用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(具有相同的数据类型)一样快,将不会是 。

我倾向于使用上面的第二个解决方案, rdbuf()上的<<,但这部分是由于历史原因;在STL被添加到标准 库之前,我已经习惯了 这样做(使用istrstream)。对于这个问题,你可能想要试验 istrstream和一个预先分配的缓冲区(假设你可以找到适合缓冲区大小的 )。

+0

如果源流是可搜索的,则可以通过执行source来获取它的大小。 seekg(0,标准::的ios_base ::端); std :: streampos pos = source.tellg(); source.seekg(0,标准::的ios_base :: BEG);'。在此之后,如果'source'仍然是OK并且'pos!= - 1','pos'将是例如文件的大小。我过去曾用过这个。 – sbi

+0

@sbi这将在大多数Unix实现上起作用,但在Windows上不起作用,至少如果文件是以文本模式打开的话。甚至不能保证编译。 –

+0

@詹姆斯:你能详细说明一下吗?我知道我在一个跨平台的应用程序中使用它,我认为它适用于Win32,OSX,BSD,Linux,Solaris和其他一些应用程序。 – sbi