如何清除stringstream变量?

我已经尝试了好几种方法,

std::stringstream m;
m.empty();
m.clear();

这两种方法都行不通。

482200 次浏览
m.str("");

似乎有用。

对于所有标准库类型,成员函数empty()是一个查询,而不是命令,也就是说,它的意思是“你是空的吗?”而不是“请扔掉你的内容”。

clear()成员函数继承自ios,用于清除流的错误状态,例如,如果一个文件流的错误状态设置为eofbit(文件结束),那么调用clear()将错误状态设置为goodbit(没有错误)。

要清除stringstream的内容,使用:

m.str("");

是正确的,尽管使用:

m.str(std::string());

在技术上更有效,因为您避免调用接受const char*std::string构造函数。但是现在任何编译器都应该能够在这两种情况下生成相同的代码——所以我只会选择可读性更好的。

在gnu c++中,这些函数不会丢弃stringstream中的数据

    m.str("");
m.str() = "";
m.str(std::string());

以下为我清空stringstream:

    m.str().clear();

这应该是最可靠的方式,不管编译器:

m=std::stringstream();

我的观点是:

这似乎在xcode和dev-c++中为我工作,我有一个菜单形式的程序,如果按照用户的请求迭代执行,将填充一个stringstream变量,这将在第一次运行代码时工作,但不会在下一次用户运行相同的代码时清除stringstream。但是下面的两行代码最终每次都在填充字符串变量之前清除了stringstream变量。(2个小时的试错和谷歌搜索),顺便说一下,单独使用每一行不会达到目的。

//clear the stringstream variable


sstm.str("");
sstm.clear();


//fill up the streamstream variable
sstm << "crap" << "morecrap";

我一直在定义它的范围:

{
std::stringstream ss;
ss << "what";
}


{
std::stringstream ss;
ss << "the";
}


{
std::stringstream ss;
ss << "heck";
}

您可以在一行中清除错误状态清空stringstream

std::stringstream().swap(m); // swap m with a default constructed stringstream

这将有效地将m重置为默认构造状态,即它实际上删除了字符串流分配的缓冲区,并重置了错误状态。下面是一个实验证明:

int main ()
{
std::string payload(16, 'x');
    

std::stringstream *ss = new std::stringstream; // Create a memory leak
(*ss) << payload;                              // Leak more memory
    

// Now choose a way to "clear" a string stream
//std::stringstream().swap(*ss); // Method 1
//ss->str(std::string());        // Method 2
    

std::cout << "end" << std::endl;
}

Demo

当使用地址杀毒器编译demo时,会显示内存使用情况:

=================================================================
==10415==ERROR: LeakSanitizer: detected memory leaks


Direct leak of 392 byte(s) in 1 object(s) allocated from:
#0 0x510ae8 in operator new(unsigned long) (/tmp/1637178326.0089633/a.out+0x510ae8)
#1 0x514e80 in main (/tmp/1637178326.0089633/a.out+0x514e80)
#2 0x7f3079ffb82f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291


Indirect leak of 513 byte(s) in 1 object(s) allocated from:
#0 0x510ae8 in operator new(unsigned long) (/tmp/1637178326.0089633/a.out+0x510ae8)
#1 0x7f307b03a25c in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned long) (/usr/local/lib64/libstdc++.so.6+0x13725c)
#2 0x603000000010  (<unknown module>)


SUMMARY: AddressSanitizer: 905 byte(s) leaked in 2 allocation(s).

如果你问我,我觉得这很离谱。为了容纳16字节的有效负载,我们花费了905字节……字符串流不是玩具。内存分配分为两部分:

  • 构造的字符串流(392字节)
  • 有效负载所需的额外缓冲区(513字节)。无关的大小与流选择的分配策略有关,对于有效负载<= 8字节,可以使用初始对象中的块。

如果您启用方法1(这个答案中显示的),额外的513(有效负载)字节将被回收,因为流是实际上清除

如果您按照评论或其他答案中建议的使method2,您可以看到在我们退出时,所有905个字节都被使用了。

就程序语义而言,人们可能只关心流是否“出现”;和“;behaves"为空,类似于vector::clear可能保持容量不变,但将vector呈现为空(当然vector在这里只花费16个字节)。考虑到字符串流需要的内存分配,我可以想象这种方法通常更快。# EYZ1。根据您的用例(流的数量,它们持有的数据,清除的频率),您可以选择最佳的方法。

最后请注意,如果不清除错误状态所有继承的状态,则清除流很少有用。这个答案中的一行代码同时做了这两件事。

这是一个概念问题。

Stringstream是一个流,所以它的迭代器是向前的,不能返回。在输出stringstream中,需要flush()来重新初始化它,就像在任何其他输出流中一样。

还有许多其他“工作”的答案,但它们经常做不必要的复制或重新分配内存。

  1. 交换流意味着您需要丢弃其中一个流,浪费内存分配。分配一个默认构造的流也是一样,

  2. 在字符串缓冲区中为字符串赋值(通过stringstream::strstringbuf::str)可能会丢失字符串已经分配的缓冲区。

清除字符串流的规范方法是:

void clear(std::stringstream &stream)
{
if (stream.rdbuf()) stream.rdbuf()->pubseekpos(0);
}

获取流缓冲区中数据大小的规范方法是:

std::size_t availSize() (const std::stringstream& stream)
{
if (stream.rdbuf())
return std::size_t(
stream.rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::out));
else
return 0;
}

将数据从流复制到其他一些预先分配的缓冲区,然后将其清除的规范方法是:

std::size_t readAndClear(std::stringstream &stream, void* outBuf, std::size_t outSize)
{
auto const copySize = std::min(availSize(stream), outSize);
if (!copySize) return 0; // takes care of null stream.rdbuf()


stream.rdbuf()->sgetn(outBuf, copySize);
stream.rdbuf()->pubseekpos(0); // clear the buffer


return copySize;
}

我希望这是一个规范的答案。语言律师,欢迎加入。