C ++ getline()的无证行为

时间:2015-08-30 15:35:52

标签: c++ stream getline stringstream istringstream

在C ++中,当你在字符串流上使用带有分隔符的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

问题

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

感谢您的回答并澄清:)

2 个答案:

答案 0 :(得分:2)

标准(C ++11§21.4.8.9¶7-10)中明确记录了getline的行为,这是关于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<>::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

回答你的问题:

  找不到

分隔符=&gt;然后只返回整个字符串/其余部分

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

  

有分隔符,但在它之前没有任何内容=&gt;返回空字符串

这只是第二点的一个特例 - 当找到分隔符时,提取终止(traits::eq(c, delim)通常归结为c==delim),即使之前没有提取过其他字符。

  

获得不存在的东西=&gt;返回可以用它读取的最后一件事

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

答案 1 :(得分:1)

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

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

  

表现为UnformattedInputFunction,但input.gcount()不受影响。构造并检查岗哨对象后,执行以下操作:

“构造和检查哨兵”意味着如果在流上检测到错误条件,则该函数将返回而不执行任何操作。这就是为什么在#3中,当“什么都不存在”时,你会观察到最后一个有效的输入。

  

1)调用str.erase()

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

  

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

     

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

这是一个错误条件,导致后续string getline局部变量不变。

它还允许您在结束之前观察输入的最后一段,因此如果您愿意,可以将文件结束视为分隔符。

  

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

     

c)存储了str.max_size()字符,在这种情况下,getline设置failbit并返回。

     

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