问题描述
在这个最小的例子中,stringstream 的输入和之前使用的 cout 的内容之间有一个奇怪的混乱:
在线gdb: https://onlinegdb.com/itO69QGAE
代码:
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
const char sepa[] = {':',' '};
const char crlf[] = {'\r','\n'};
int main()
{
cout<<"Hello World" << endl;
stringstream s;
string test1 = "test_01";
string test2 = "test_02";
s << test1;
cout << s.str() << endl;
// works as expected
// excpecting: "test_01"
// output: "test_01"
s << sepa;
cout << s.str() << endl;
// messing up with prevIoUs cout output
// expecting: "test_01: "
// output: "test_01: \nHello World"
s << test2;
cout << s.str() << endl;
// s seems to be polluted
// expecting: "test_01: test_02"
// output: "test_01: \nHello Worldtest_02"
s << crlf;
cout << s.str() << endl;
// once again messing up with the cout content
// expecting: "test_01: test_02\r\n"
// output: "test_01: Hello Worldtest_02\r\nHello World"
return 0;
}
所以我想知道为什么会这样?
因为它只发生在一个 char 数组被推入 stringstream 时,它很可能是关于这个......但根据参考,stringstream 的“
除此之外,stringstream 和 cout 之间似乎存在(?隐藏的,或至少不明显的?)关系。那么为什么内容会污染到stringstream?
在这个例子中是否有任何错误/愚蠢的用法或狗被埋在哪里(-> 德国成语 :P )?
最好的问候和感谢 达米安
附言我的问题不是关于“修复”这个问题,比如使用字符串而不是字符数组(这会起作用)……这是关于理解内部机制以及为什么这实际上会发生,因为对我来说这只是一种意外行为。
解决方法
std::stringstream::str()
函数返回一个字符串,其中包含之前写入流中的所有字符,在之前所有调用到 operator<<
(或其他输出功能)。但是,您似乎希望只返回最后一个输出操作 - 事实并非如此。
这类似于例如std::cout
有效:每次调用 std::cout <<
都会将字符串附加到标准输出;它不会清除控制台的屏幕。
要实现您想要的效果,您需要每次都使用单独的 std::stringstream
实例:
std::stringstream s1;
s1 << test1;
std::cout << s1.str() << std::endl;
std::stringstream s2;
s2 << sepa;
std::cout << s2.str() << std::endl;
或者更好的是,使用 std::stringstream
函数的单参数重载清除 str()
的内容:
std::stringstream s;
s << test1;
std::cout << s.str() << std::endl;
// reset the contents of s to an empty string
s.str("");
s << sepa;
std::cout << s.str() << std::endl;
s.str("")
调用有效地丢弃了之前写入流中的所有字符。
请注意,即使 std::stringstream
包含一个看起来更合适的 clear()
函数,它也不类似于例如std::string::clear()
或 std::vector::clear()
并且不会产生您的情况所需的效果。
我又来了,
感谢“一些程序员老兄”的评论,我想我明白了:
由于没有与两个 char 数组相关的 (null-)termination-symbol,因此 stringstream-<<
-operator 似乎会一直插入,直到它遇到空终止符 '\0'
。
要么用 \0
符号(例如 const char sepa[] = {':',' ','\0'}
)扩展两个数组,要么用例如终止长度s << string(sepa,2)
将执行预期的输出。
在上述特定情况下,数据似乎在内存中对齐,因此将在 cout << "Hello World"
语句中找到下一个空终止符。由于无法保证这种对齐方式,因此当缺少终止符时,这实际上会导致未定义的行为。
因此还有两个额外的“终止”数组,例如 const char sepa[] = {':',' '}; char[] end_of_sepa = {'\0'};
在提到的数组之后立即声明将导致预期的输出,尽管其余部分将保持不变...但这可能无法保证,取决于内存中的内部表示。
附言如前所述,这个问题不是关于修复而是关于理解。所以请随时确认或更正我的假设。
编辑:更正了粗体代码部分。