写这篇blog的起因在于:
function getline()在不指定休止符时,默认以换行作为休止符。
由于我的认识上的不足,一度认为
function cin.getline()的使用中将调用改函数之前的一个回车也进行了读取,导致数据转变的时候出错
可见于http://blog.csdn.net/qq_30490125/article/details/51308877
的Row95和Row98。
后在vs2010中也做了如下的测试,发现直接输入回车后进行了换行输出。
char a[100];
memset(a,0,sizeof(a));
cin.getline(a,100);
cout<<"start"<<endl;
cout<<a<<endl;
cout<<"end"<<endl;
在调用std下的function getline()时也发生了同样的情况
string a;
getline(cin,a);
cout<<"start"<<endl;
cout<<a<<endl;
cout<<"end"<<endl;
解决方案:直接在getline()之前进行了调用了get(),将多余的回车吃掉。
分析部分:
istream中function getline()的实现:
_Myt& __CLR_OR_THIS_CALL getline(_Elem *_Str, streamsize _Count)
{ // get up to _Count characters into NTCS, discard newline
return (getline(_Str, _Count, _Myios::widen('/n')));
}
这里是两个参数的getline()的入口。
std::istream::getline
原型:
istream& getline (char* s, streamsize n );
istream& getline (char* s, streamsize n, char delim );
detail:http://www.cplusplus.com/reference/istream/istream/getline/
可以看到,在调用两个参数的getline时,实际上是函数为我们添加了第三个参数。
接下来调用了如下的函数:
_Myt& __CLR_OR_THIS_CALL getline(_Elem *_Str,
streamsize _Count, _Elem _Delim)
{ // get up to _Count characters into NTCS, discard _Delim
_DEBUG_POINTER(_Str);
ios_base::iostate _State = ios_base::goodbit;
_Chcount = 0;
const sentry _Ok(*this, true);
if (_Ok && 0 < _Count)
{ // state okay, use facet to extract
int_type _Metadelim = _Traits::to_int_type(_Delim);
_TRY_IO_BEGIN
int_type _Meta = _Myios::rdbuf()->sgetc();// 从输入流读一个字符
for (; ; _Meta = _Myios::rdbuf()->snextc())
if (_Traits::eq_int_type(_Traits::eof(), _Meta))
{ // end of file, quit
_State |= ios_base::eofbit;// 遇到文件尾,getline结束
break;
}
else if (_Meta == _Metadelim) //休止符的判断
{ // got a delimiter, discard it and quit
++_Chcount;
_Myios::rdbuf()->sbumpc();// 这句把结束符读掉了,如果不指定结束符,那就是把'/n'读掉了
break;
}// 遇到结束符,getline结束,注意这里的顺序,它是先判断是否遇到结束符,后判断是否读入了指定个数的。在这里计数器仍然执行++操作
else if (--_Count <= 0)
{ // buffer full, quit
_State |= ios_base::failbit;
break;
}// 读到了指定个数,执行到这里已经隐含了在指定个数的最后一位仍然不是结束符,由于在执行这部分时,在对长度计数器没有进行++操作
else
{ // got a character, add it to string
++_Chcount;
*_Str++ = _Traits::to_char_type(_Meta);
}
_CATCH_IO_END
}
*_Str = _Elem(); // add terminating null character
_Myios::setstate(_Chcount == 0 ? _State | ios_base::failbit : _State);
return (*this);
}
从实现当中我们可以看到,我之前的调用的确被正确执行了,当我直接输入回车时,getline()函数被调用,但有与第一个字符就是休止符,通过调用sbumpc()将指针指向了下一个字符后跳出循环,此时_Str当中什么都没有被存入,我们通过调用
*_Str = _Elem(); // add terminating null character
将_Str首位置为空字符’\0’。
接下来对_Str进行了输出,输出为空。
std::getline (string)
原型:
(1)
istream& getline (istream& is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim);
(2)
istream& getline (istream& is, string& str);
istream& getline (istream&& is, string& str);
detail:http://www.cplusplus.com/reference/string/string/getline/
从输入流冲获取获取数据直到遇到delim休止符或文件结束符,将获得的数据放入到str当中,
关于读取到休止符的处理可以参考之前的_Myt& __CLR_OR_THIS_CALL getline()。
我们知道c++的string有类似vector的机制,会事先申请一个固定大小的空间,当空间不足时会重新申请一块更大的空间,将原来的数据复制进新的空间。
string a="fyb";
string b;
b=a;
a.push_back('b');
cout<<a<<endl;
cout<<&a<<endl;
cout<<b<<endl;
cout<<&b<<endl;
std::getline()实质就是每次将一个字符添加string的末尾,所以中间可能会存在内存空间的申请,和string数据的复制。
std::istream::get
原型:
single character (1)
int get();
istream& get (char& c);
c-string (2)
istream& get (char* s, streamsize n);
istream& get (char* s, streamsize n, char delim);
stream buffer (3)
istream& get (streambuf& sb);
istream& get (streambuf& sb, char delim);
detail:http://www.cplusplus.com/reference/istream/istream/get/
单字符的get可以进行逐字符地读入,类似getchar();
这里我们主要说的还是第二类,这里的应用跟getline及其类似,但get有一点需要注意,getline会将休止符一起读入后舍弃,但get遇到休止符就停止了,输入流队列当中仍然保留有休止符。