【问题标题】:C++ - Does resetting stringstream not reset the get position or clear flags?C++ - 重置字符串流不会重置获取位置或清除标志吗?
【发布时间】:2019-12-12 13:53:50
【问题描述】:

我在循环中从字符串流中提取值,每次执行循环时都重置循环顶部的字符串流。但是,字符串流的>> 运算符每次都在第二次迭代中失败。这个代码的提炼版本重现了我遇到的问题:

istringstream word;
string in;
int number;

while(cin >> in) {

   word.str(in);

   //Uncommenting either one of the following lines seems to solve the issue:
   //word.clear();
   //word.seekg(0);

   word >> number;

   if(word.fail()) {
       cerr << "Failed to read int" << endl;
       return 1;
   }

   cout << in << ' ' << number << endl;
}

就目前而言,它总是在第二次循环迭代时失败。但是,取消注释两行代码中的任何一行都可以解决问题。我不明白的是,既然我已经用word.str(in) 重置了字符串流,为什么它仍然失败?为什么重置获取位置可以解决问题?

我是否遗漏了有关字符串流的工作原理的信息?它是否在最后一次有效读取而不是由于 EOF 而失败的读取上设置了eofbit 标志?如果是这样,为什么seekg(0) 似乎清除了该标志,而重置字符串流却没有?

【问题讨论】:

  • 既然你只在循环内使用word,为什么不在循环范围内(即循环内)定义它呢?一个好的经验法则是在尽可能接近其使用点的地方定义变量(这意味着您也应该在阅读之前定义 number)。
  • 可能重复:C++ - repeatedly using istringstream(调用 word.str() 不会重置 iostatus 位)
  • 我相信它只是在缓冲区上调用 str(s) ,不确定所有具有 en.cppreference.com/w/cpp/io/basic_stringbuf/str 的效果。可能错误标志与缓冲区完全分开,但我不确定。
  • @Adrian-ReinstateMonica 我认为“你需要打电话给clear()”和“为什么我必须打电话给clear?”是有区别的。 OP 有clear()/working 解决方案。
  • @FireLancer 这就是为什么我说可能重复! ??????

标签: c++ stringstream


【解决方案1】:

如果你看一下流的状态,这应该会更清楚一些。

int main()
{
    std::vector<std::string> words = { "10", "55", "65" };
    std::istringstream word;
    for (const auto &in : words)
    {
        word.str(in);
        std::cout << "stream state:"
            << (word.rdstate() & std::ios::badbit ? " bad" : "")
            << (word.rdstate() & std::ios::failbit ? " fail" : "")
            << (word.rdstate() & std::ios::eofbit ? " eof" : "")
            << std::endl;
        int number;
        word >> number;

        if (word.fail()) {
            std::cerr << "Failed to read int" << std::endl;
            return 1;
        }

        std::cout << in << ' ' << number << std::endl;
        std::cout << "stream state:"
            << (word.rdstate() & std::ios::badbit ? " bad" : "")
            << (word.rdstate() & std::ios::failbit ? " fail" : "")
            << (word.rdstate() & std::ios::eofbit ? " eof" : "")
            << std::endl;
    }
}

这将导致:

流状态: 10 10 流状态:eof 流状态:eof 无法读取 int

所以最初没有设置任何标志,但读取数字到达流的末尾,设置eofbitstd::istringstream::str 被定义为好像调用rdbuf()-&gt;str(new_str)。这并没有说明清除标志。

调用clear() 当然会清除eofbit,但调用seekg在 C++11 或更高版本中也是如此。 " 在做任何其他事情之前, seekg 清除 eofbit。 (C++11 起)”。

请注意,如果您说“10 20”,它只会丢弃“20”,而不会检测到错误。

流状态: 10 20 10 流状态: 流状态: 55 55 流状态:eof 流状态:eof 无法读取 int

因此,您实际上可能想检查该 eof 标志以查看您是否阅读了整个流。

正如其他人所指出的,当然,每个循环都构造一个新的流对象也意味着不必担心以前的标志/状态。

【讨论】:

  • 因此,与 fstream 不同,stringstream 本质上是提前查找 eof 并在最后一次有效读取中设置 eofbit
  • @FahimFaisal 不确定您的区别是什么, fstream 设置 eof 相同。因为它必须触摸流/文件的末尾才能看到数字是否完成,否则如果它从字面上只读取“10”,它怎么知道它实际上不是“100”?
  • ifstream infile { "myfile.txt" }; string word; while (infile &gt;&gt; word) { cout &lt;&lt; word &lt;&lt; endl; if(infile.eof()) cout &lt;&lt; "eof" &lt;&lt; endl; cout &lt;&lt; word &lt;&lt; endl; }
  • 您确定文件中没有额外的换行符吗?例如rextester.com/TVOC70941(我不知道一个易于使用的具有多个文件的在线 C++,但如果文件 完全 作为字符串,则使用 ifstream 给我相同的结果)。
  • 哦,原来我确实有一个尾随换行符。没有它,它的行为方式与字符串流相同,应该如此。非常感谢!
【解决方案2】:

正如@Someprogrammerdude 建议的那样:只需将istringstream 移动到while 循环中(您可以将其更改为for 循环以将in 也保留在循环中):

for (string in; cin >> in;)
{
    istringstream word(in);
    int number;
    if (!(word >> number))
    {
        cerr << "Failed to read int" << endl;
        return 1;
    }
    cout << in << ' ' << number << endl;
}

这样它会重新创建每个循环。

当您使用它时,也将number 移入其中(当然,除非您在循环之外使用它)。

【讨论】:

  • 我个人更愿意使用istringstream word(in);,而不是单独调用str。也可能使用if (!(word &gt;&gt; number)) 而不是单独提取和fail 检查。 ;)
  • @Someprogrammerdude 我永远不会徒劳地使用你的名字。固定的! :-)
猜你喜欢
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多