C++重载输入和输出操作符以及IO标准库中的刷新输入缓冲区残留字符问题

 

C++重载输入和输出操作符以及IO标准库中的刷新输入缓冲区残留字符问题

分类: c++   60人阅读  评论(0)  收藏  举报

今天在做C++ Primer习题的14.11时,印象中应该挺简单的一题,结果却费了很长时间。

类定义:

[cpp]  view plain copy
  1. typedef string Date;  
  2.   
  3. class CheckoutRecord{  
  4. public:  
  5.     CheckoutRecord(){book_id=-1;}  
  6.     friend ostream& operator<<(ostream &os,const CheckoutRecord &obj);  
  7.     friend istream& operator>>(istream &in,CheckoutRecord &obj);  
  8. private:  
  9.     double book_id;  
  10.     string title;  
  11.     Date date_borrowed;  
  12.     Date date_due;  
  13.     pair<string,string> borrower;  
  14.     vector<pair<string,string>*>wait_list;  
  15. };  

重载输出操作符很简单:

[cpp]  view plain copy
  1. ostream& operator<<(ostream &os,const CheckoutRecord &obj)  
  2. {  
  3.     os<<"Book ID:"<<obj.book_id<<endl;  
  4.     os<<"Title  :"<<obj.title<<endl;  
  5.     os<<"Data borrowed:"<<obj.date_borrowed<<endl;  
  6.     os<<"Date due     :"<<obj.date_due<<endl;  
  7.     os<<"Borrower     :"<<obj.borrower.first<<" "<<obj.borrower.second<<endl;  
  8.     os<<"Waiters for this book:"<<endl;  
  9.     for(unsigned int i=0;i<obj.wait_list.size();++i)  
  10.         os<<obj.wait_list[i]->first<<" "<<obj.wait_list[i]->second<<";";  
  11.     os<<endl;  
  12.     return os;  
  13. }  


重载输入操作符复杂一点,因为我们要考虑用户输入错误的情况:

[cpp]  view plain copy
  1. istream& operator>>(istream &in,CheckoutRecord &obj)  
  2. {  
  3.     in>>obj.book_id;  
  4.     if(in){  
  5.         //when you typed newline,the new line was added to the   
  6.         //stream buffer, but "in>>obj.book_id" only reads the first double data and   
  7.         // left '\n' character still in the input stream buffer.  
  8.         in.ignore(INT_MAX,'\n');  
  9.         //Title may contain spaces  
  10.         std::getline(in,obj.title);  
  11.   
  12.         in>>obj.date_borrowed>>obj.date_due>>\  
  13.             obj.borrower.first>>obj.borrower.second;  
  14.   
  15.         while(in){  
  16.             pair<string,string> *waiter=new pair<string,string>;  
  17.             in>>waiter->first;  
  18.             if(waiter->first=="end"){  
  19.                 delete waiter;  
  20.                 break;  
  21.             }  
  22.             in>>waiter->second;  
  23.             obj.wait_list.push_back(waiter);  
  24.         }  
  25.     }  
  26.     else  
  27.         obj=CheckoutRecord();  
  28.     return in;  
  29. }  

之所以费了很长时间,主要因为前面少写了一行代码:
[cpp]  view plain copy
  1. in.ignore(INT_MAX,'\n');  
[cpp]  view plain copy
  1. 导致后面的getline得到的是空行。  

为什么要加上这样一行,我在注释中已经写明了原因。以前一直以为"cin>>whatever_data"这类输入语句,碰到换行或是空白这些分隔符的时候,会在流缓冲中去除有效输入后的下一个分隔符,原来这些分隔符都还在保存在缓冲中!输入语句的行为应该是这样,只会在流中消除在它要的有效输入的前面的分隔符字符,得到想要的输入后,后面又碰到一个分隔符,说明该输入数据结束。但不对想要的输入数据后面的分隔符做任何处理

不仅是对分隔符,对于输入错误时,"cin>>.."语句在输入终止后,使输入错误的字符让保留在流缓冲区内,这是一定要用cin.ignore来清理。

8.2节中的例子:

[cpp]  view plain copy
  1. int ival;  
  2. // read cin and test for only EOF; loop is excuted even if there are other IO failures  
  3. while(cin >> ival, !cin.eof()){  
  4.     if(cin.bad()) // input stream is corrupted; bail out  
  5.         throw runtime_error("IO stream corrupted!");  
  6.     if(cin.fail()){  
  7.         cerr << "Bad data, try again!";  
  8.         cin.clear();  
  9.         cin.ignore();     
  10.         continue;  
  11.     }  
  12. }  

不仅是C++的标准IO库,C中的标准IO库也用同样的问题——如果在scanf后面直接调用getline,也会得到空行。而且,对应cin.ignore(),可用fflush(stdin)来刷新缓冲区。

那哪些输入函数会自动处理有效输入后的换行符呢?

getline和fgets这类以行为单位的输入函数,会自动将已输入行的换行符从输入缓冲中去除。

 

这个教训再次告诉我们,使用函数接口的时候,一定要理清它们的行为细节。有时还要弄清它们的底层实现,才能更好地理解它们的行为

 

最后给出main函数和测试用例:

[cpp]  view plain copy
  1. /* 
  2. Sample Input: 
  3.  
  4. 1001 
  5. Miserable World 
  6. 201308 
  7. 201309 
  8. Simon Smith 
  9. Mike a 
  10. Kris b 
  11. Tom  c 
  12. Bison d 
  13. Jumping e 
  14. end 
  15.  
  16. err_id 
  17. Miserable World 
  18. 201308 
  19. 201309 
  20. Simon Smith 
  21. Mike a 
  22. Kris b 
  23. Tom  c 
  24. Bis d 
  25. Jump e 
  26. end 
  27.  
  28. */  
  29. int _tmain(int argc, _TCHAR* argv[])  
  30. {  
  31.     CheckoutRecord record;  
  32.     cin>>record;  
  33.     cout<<"========================================"<<endl;  
  34.     cout<<record;  
  35. }  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值