我需要将一个文件复制到字符串中。我需要一种方式来为该字符串对象预分配内存,并且需要一种方法将文件内容直接读取到该字符串的内存中。
std::string
有一个 .reserve
方法 用于预分配空间。
std::string s;
s.reserve(1048576); // reserve 1 MB
read_file_into(s);
std::readline()
也有帮助吗? - Melroy van den Berg这并不是一个直接的答案,而是对其他答案进行的评论/总结/比较(同时也是为什么我推荐@ Johannes-litb在他的答案中给出的代码风格的快速演示)。由于@sbi发布了一个看起来非常好的替代方案,并且(特别是)避免了涉及读取到stringstream,然后使用.str()
成员来获取字符串的额外复制,所以我决定快速比较一下两者:
[编辑:我添加了第三个测试用例,使用@Tyler McHenry的基于istreambuf_iterator
的代码,并添加了一行打印每个读取的字符串的长度,以确保优化器没有因为结果从未被使用而优化掉读取操作。]
[编辑2:现在,马丁·约克(Martin York)的代码也已添加...]
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <iterator>
#include <time.h>
int main() {
std::ostringstream os;
std::ifstream file("equivs2.txt");
clock_t start1 = clock();
os << file.rdbuf();
std::string s = os.str();
clock_t stop1 = clock();
std::cout << "\ns.length() = " << s.length();
std::string s2;
clock_t start2 = clock();
file.seekg( 0, std::ios_base::end );
const std::streampos pos = file.tellg();
file.seekg(0, std::ios_base::beg);
if( pos!=std::streampos(-1) )
s2.reserve(static_cast<std::string::size_type>(pos));
s2.assign(std::istream_iterator<char>(file), std::istream_iterator<char>());
clock_t stop2 = clock();
std::cout << "\ns2.length = " << s2.length();
file.clear();
std::string s3;
clock_t start3 = clock();
file.seekg(0, std::ios::end);
s3.reserve(file.tellg());
file.seekg(0, std::ios::beg);
s3.assign((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
clock_t stop3 = clock();
std::cout << "\ns3.length = " << s3.length();
// New Test
std::string s4;
clock_t start4 = clock();
file.seekg(0, std::ios::end);
s4.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&s4[0], s4.length());
clock_t stop4 = clock();
std::cout << "\ns4.length = " << s3.length();
std::cout << "\nTime using rdbuf: " << stop1 - start1;
std::cout << "\nTime using istream_iterator: " << stop2- start2;
std::cout << "\nTime using istreambuf_iterator: " << stop3 - start3;
std::cout << "\nTime using read: " << stop4 - start4;
return 0;
}
现在让我们来看一下令人印象深刻的结果。首先是使用VC++(以防有人关心,Martin的代码足够快,我增加了文件大小以获得有意义的时间):
s.length() = 7669436
s2.length = 6390688
s3.length = 7669436
s4.length = 7669436
使用rdbuf的时间:184
使用istream_iterator的时间:1332
使用istreambuf_iterator的时间:249
使用read的时间:48
然后是使用gcc(cygwin):
s.length() = 8278035
s2.length = 6390689
s3.length = 8278035
s4.length = 8278035
使用rdbuf的时间:62
使用istream_iterator的时间:2199
使用istreambuf_iterator的时间:156
使用read的时间:16
[编辑结束--结论保持不变,尽管获胜者已经改变--Martin的代码显然是最快的。]
就速度最快和最慢而言,这些结果相当一致。唯一的不一致之处在于一个比另一个的速度差异有多大。虽然它们的排名相同,但是使用gcc的速度差异比使用VC++的速度差异大得多。
istreambuf_iterator
来避免每个字符读取时跳过空格 - 或许这会加速事情,因为它是在更低的层次上发生的?),后者需要经过多个步骤,包括op++、op*等。但我没想到这会有那么大的差别。感谢你的计时! - Johannes Schaub - litb这应该就是你所需要的:
ostringstream os;
ifstream file("name.txt");
os << file.rdbuf();
string s = os.str();
这段代码从file
读取字符并将它们插入到字符串流中。之后,它获取在幕后创建的字符串。请注意,我陷入了以下陷阱:使用提取运算符将跳过初始空格。您必须像上面那样使用插入运算符,或使用noskipws
操作器:
// Beware, skips initial whitespace!
file >> os.rdbuf();
// This does not skip it
file >> noskipws >> os.rdbuf();
这些函数被描述为逐个字符读取流(虽然不确定在这里有哪些优化可能性),我还没有计时以确定它们的速度。
ostringstream
缓冲区,另一次到 s
。 - Ramadheer Singhifstream.read(&my_str[0], length)
是一个块读取,假设字符串对象在内存的连续块中,但这个假设太危险了吗?还是很实用的呢? - Ramadheer Singh&my_str[0]
指向一段连续的内存区域。他在http://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/中说道:“然而,当前的ISO C++确实要求&str[0]提供指向连续字符串数据的指针(但不一定是以null结尾的!),因此实现者没有太多余地来使用非连续的字符串。”,尽管当前标准中关于字符串的措辞在这方面存在问题(引用了一个非const的data()
- 但它并不存在!)- 因此不能依赖它。 - Johannes Schaub - litb仅供娱乐,这是另一种实现方式:
// Beware, brain-compiled code ahead!
std::ifstream ifs( /* ... */ );
if( !ifs.good() ) return; // whatever
std::string str;
ifs.seekg( 0, std::ios_base::end );
const std::streampos pos = ifs.tellg();
ifs.seekg( 0, std::ios_base::beg );
if( pos!=std::streampos(-1) ) // can get stream size?
str.reserve(static_cast<std::string::size_type>(pos));
str.assign( std::istream_iterator<char>(ifs)
, std::istream_iterator<char>() );
}
是可以接受的 ;) - Ramadheer Singhseekg()
行(它将一个字符串填充为文件的内容),并忽略了{
。我已经修复了它,但无论如何,这就是那个免责声明的作用。 - sbistd::string::resize()
实际上会分配所需的空间。
std::string::reserve()
可能不会分配空间(它只是一个请求)。
看起来你想知道如何在std::string中实现类似于CString::GetBuffer, ReleaseBuffer的操作。
我不知道是否有直接的方法来做到这一点,一个简单的方法是创建一个原始的C风格缓冲区,将数据读入缓冲区,然后使用assign或其他方式将缓冲区复制到std::string中。当然,你需要考虑缓冲区溢出等问题,此外我会使用std::autoptr来管理原始缓冲区指针,以确保在异常情况下进行释放。这比使用stringstream等方法要简单一些。如果需要,我可以提供一个示例。
Devin Ellingson
auto_ptr
不能正确处理数组类型。只需使用 std::vector
即可。(此外,在帖子下签名是不被赞同的;你的名字已经在你所做的一切下面了。) - GManNickG