【问题标题】:C++ getline()'s undocumented behaviorC++ getline() 的未记录行为
【发布时间】:2015-11-24 17:17:12
【问题描述】:

在 C++ 中,当您在 stringstream 上使用带有分隔符的 getline() 时,几乎没有我发现记录在案的事情,但在以下情况下它们有一些非错误的方便行为:

  • 没有找到分隔符 => 然后简单地返回整个字符串/其余部分
  • 有分隔符,但之前没有任何内容 => 返回空字符串
  • 得到不存在的东西 => 返回最后可以用它读取的东西

一些测试代码(简化):

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

string test(const string &s, char delim, int parseIndex ){
    stringstream ss(s);
    string parsedStr = "";
    
    for( int i = 0; i < (parseIndex+1); i++ ) getline(ss, parsedStr, delim);
    
    return parsedStr;
}

int main() {
    stringstream ss("something without delimiter");
    string s1;
    getline(ss,s1,';');
    cout << "'" << s1  << "'" << endl; //no delim
    cout << endl;
    
    string s2 = "321;;123";
    cout << "'" << test(s2,';',0) << "'" << endl; //classic
    cout << "'" << test(s2,';',1) << "'" << endl; //nothing before
    cout << "'" << test(s2,';',2) << "'" << endl; //no delim at the end
    cout << "'" << test(s2,';',3) << "'" << endl; //this shouldn't be there
    cout << endl;
    
    return 0;
}

测试代码输出:

'something without delimiter'

'321'
''
'123'
'123'

测试代码小提琴:http://ideone.com/ZAuydR

问题

问题是 - 这可以依靠吗?如果是这样,它记录在哪里 - 是吗?

感谢您的回答和澄清:)

【问题讨论】:

  • 您找到了哪些文档?是的,所有行为都完全记录在 C++ 标准中。
  • 它的记录也大致相同here。

标签: c++ stream getline stringstream istringstream


【解决方案1】:

getline 的行为明确记录在标准 (C++11 §21.4.8.9 ¶7-10) 中,这是有关 C++ 的唯一规范性文档。

您在前两个问题中询问的行为是有保证的,而第三个问题是您的测试设备制造方式的结果。

template<class charT, class traits, class Allocator>
  basic_istream<charT,traits>&
    getline(basic_istream<charT,traits>& is,
            basic_string<charT,traits,Allocator>& str,
            charT delim);
template<class charT, class traits, class Allocator>
   basic_istream<charT,traits>&
   getline(basic_istream<charT,traits>&& is,
           basic_string<charT,traits,Allocator>& str,
           charT delim);

效果: 表现为无格式输入函数(27.7.2.3),只是它不影响值 由后续调用 basic_istream&lt;&gt;::gcount() 返回。在构造了sentry 对象之后, 如果sentry 转换为true,则调用str.erase(),然后从is 中提取字符并附加 他们到str 就像调用str.append(1, c) 一样,直到发生以下任何情况:

  • 文件结束出现在输入序列上(在这种情况下,getline 函数调用is.setstate(ios_base::eofbit))。
  • traits::eq(c, delim) 用于下一个可用的输入字符 c(在这种情况下,c 被提取但 未附加)(27.5.5.4)
  • 存储str.max_size() 字符(在这种情况下,函数调用is.setstate(ios_base::failbit))(27.5.5.4)

条件按所示顺序进行测试。无论如何,在提取最后一个字符后, 哨兵对象k 被销毁。

如果函数没有提取字符,它会调用is.setstate(ios_base::failbit),这可能会抛出 ios_base::failure (27.5.5.4)。

返回: is.

回答你的问题:

没有找到分隔符 => 然后简单地返回整个字符串/其余部分

这是第一个退出条件的结果 - 当输入字符串终止时,字符串流进入文件末尾,因此提取终止(在将所有前面的字符添加到输出字符串之后)。

有分隔符,但之前没有任何内容=>返回空字符串

这只是第二点的一种特殊情况——即使之前没有提取过其他字符,提取也会在找到分隔符时终止(traits::eq(c, delim) 通常归结为c==delim)。

得到不存在的东西=>返回最后可以用它读取的东西

情况并非完全如此。如果流处于错误状态(sentry 对象未转换为true,在上面的描述中)-在您的情况下,您有一个 EOF-,getline 将保留您的字符串并返回。在您的测试代码中,您看到最后读取的数据只是因为您正在回收相同的字符串而没有在各种测试之间清除它。

【讨论】:

  • 好的 - 到第三个 - 所以它构造了找到 EOF 的哨兵对象,所以它转换为 false 并且 str.erease() 没有被调用? (所以字符串保持不变)
  • @jave.web: 大多数情况下 - sentry 对象还可以对流执行其他几项操作,但关键是如果流处于错误状态,它会转换为 @987654348 @ 和getline 知道它必须立即退出。
【解决方案2】:

C++ 工具的行为由 ISO C++ 标准描述。但是,它不是最易读的资源。在这种情况下,cppreference.com 具有良好的覆盖率。

这是他们要说的。报价块是复制粘贴的;我已经对您的问题进行了解释。

行为与UnformattedInputFunction 相同,但input.gcount() 不受影响。构建并检查哨兵对象后,执行以下操作:

“构造和检查哨兵”意味着如果在流上检测到错误情况,函数将不做任何事情而返回。这就是为什么在 #3 中,当“什么都不应该存在”时,您会观察到最后一个有效输入。

1) 调用 str.erase()

因此,如果随后在分隔符之前没有找到任何内容,您将得到一个空字符串。

2) 从输入中提取字符并将它们附加到 str 直到发生以下情况之一(按列出的顺序检查)

a) 输入的文件结束条件,在这种情况下,getline 设置eofbit。

这是一种错误情况,会导致string 局部变量被后续的getlines 更改。

它还允许您观察结尾之前的最后一段输入,因此您可以根据需要将文件结尾视为分隔符。

b) 下一个可用的输入字符是 delim,由 Traits::eq(c, delim) 测试,在这种情况下,分隔符从输入中提取,但不附加到 str。

c) str.max_size() 字符已被存储,在这种情况下 getline 设置失败位并返回。

3) 如果由于某种原因没有提取字符(甚至没有被丢弃的分隔符),getline 设置failbit 并返回。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-31
    相关资源
    最近更新 更多