【问题标题】:Puzzling behavior of istream::getline()istream::getline() 令人费解的行为
【发布时间】:2018-07-02 23:38:26
【问题描述】:

我测试了以下代码以澄清我对istream::getline()的理解:

 #include <iostream>
    #include <sstream>
    using namespace std;
        int main()    
        {
           string s("abcd efgh\nijklmnopqrst");         
           string s1;
           stringstream ss(s);
           ss >> s1;
           cout << s1 << endl;
           ss.getline(&s1[0], 250, '\n');
           cout << s1 << endl;
           ss >> s1;
           cout << s1 << endl;
           getchar();
        return 1;
        }

然后打印控制台:

abcd
 efg
ijklmnopqrst

但我认为应该是

abcd
efgh
ijklmnopqrst

另外,我发现调用ss.getline()s1 的大小与调用ss&gt;&gt; 后的大小相同,但再次调用ss&gt;&gt; 后大小会改变。谁能帮我解析一下?

【问题讨论】:

  • ss.getline(&amp;s1[0], 250, '\n'); 导致缓冲区溢出,您将 5 个字符(+ 终止符)读入仅大小为 4 的字符串
  • 由于std::stringstream::getline() 不支持std::string,我更喜欢使用std::getline(),它可以很好地与字符串配合使用。 std::getline(ss, s1, '\n');

标签: c++ iostream getline


【解决方案1】:
ss.getline(&s1[0], 250, '\n');

getline() 调用的第一个参数是char *ss 完全不知道这个char 缓冲区实际上来自std::string,而且它实际上是它的内部缓冲区。

使整个事件复杂化的是,std::string 给人的印象是它包含四个字符。因为这就是它的全部,在这一点上。

绝对没有任何事情可能导致std::string 改变主意。仅仅因为一个指向其内部字符缓冲区的指针被传递给了getline(),它开始在它上面乱涂乱画(导致未定义的行为,我稍后会推断),std::string 仍然认为它包含只有四个字符。

同时,初始格式化输入运算符&gt;&gt; 提取了初始字符,但没有提取以下空格,因此当此流随后具有此getline() 调用,它开始提取从这个空格字符开始的字符,直到下一个换行符——五个字符(如果我指望我的手指的话),但是将它转储到一个有保证的缓冲区中,由std::string,只能容纳四个字符(因为请记住,初始格式化的提取运算符&gt;&gt;,只在其中转储了四个字符)。

我忽略了一些细节,例如std::string 负责自动跟踪尾随'\0',但最重要的是这是未定义的行为。 getline 调用提取更多字符,保证它提供的缓冲区可以容纳。未定义的行为。一大堆未定义的行为。不仅仅是第二行输出中的四个字符不是您期望看到的四个字符,而是 getline() 实际上最终提取了 more 个字符, 但是这里打印的 std::string 根据宪法完全有权利相信它仍然只有四个字符,只是它的内部缓冲区被踩满了。

【讨论】:

    【解决方案2】:

    两件事。

    首先,&gt;&gt; 不消耗空格,所以getline 将检索它。

    其次,这行不正确:

    ss.getline(&s1[0], 250, '\n');
    

    由于getline 需要std::basic_string,只需传入字符串:

    ss.getline(s1, 250, '\n');
    

    在您的代码中,&amp;s1[0] 可以访问写入的底层缓冲区,但字符串的长度是单独存储的,并且仍然是前一次读取的内容(这就是 h 被删除的原因)。不过,此时由于缓冲区溢出,您已经调用了未定义的行为。

    【讨论】:

    • 成员函数getline 不能使用std::string。但是,有一个免费的功能可以做到。
    • The getline member function of istreams 实际上并不处理std::string,仅处理char*。您想使用the std::getline function,例如std::getline(ss, s1, '\n');(注意:它不需要大小,因为它会根据需要调整底层字符串的大小)。
    • 谢谢,我确实在考虑非会员功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-30
    • 2020-06-19
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 2010-11-15
    相关资源
    最近更新 更多