有位同学问怎么读一整行键盘输入,他查到个getline函数,没有看懂,云云。
下面说明一下。
C风格的行输入
while { getc / getchar }
显然一个一个字符拼是肯定可以的,不过要注意1.字符数组不要越界;2.结尾检查要完善。
/**
* \brief while-getchar输入行
* \param[out] line 存放行的数组
* \param[in] arrSize line数组的大小(包括结尾0)
* \return 输入的字符串的长度
*/
int myGetLine(char line[], int arrSize)
{
int i, c, lineNotEnded = 1;
for (i = 0; (i < arrSize - 1) && lineNotEnded; ++i)
{
switch (c = getchar()) /* 或者 c = getc(stdin) */
{
case '\r': /* 回车——到行首 */
case '\n': /* 换行——下一行 */
case EOF: /* “文件结束符” */
line[i] = '\0';
lineNotEnded = 0; /* 这里break出不了for,所以要个标志,或者用goto */
break;
default:
line[i] = (char)c;
break;
}
}
if (lineNotEnded) { /* 说明是填满了 */
line[i] = '\0';
return i;
} else { /* 说明是读到结束了 */
return i - 1;
}
}
用scanf("%c", &c);
不好,因为无法处理EOF
情况。
gets
上面的函数还是挺复杂的。读行这么常见的事情,如果C标准里没有提供的话,就不正常了。其实char* gets(char*);
函数就足够了。这个函数不大好的地方在于,它不关心输入数组的长度,所以使用的时候最好把数组开长些,以免输入太多而越界。
C++风格的行输入
cin.getline
有两种getline
,一种是cin
的成员函数,一种是全局函数。
准确地说起来,cin
不是“函数”而是“全局变量”,类型是“类”,更准确地说是“istream
类的某个实现了>>
运算符重载的派生类”。平时大家用cin>>
,其实是调用了istream
类“重载”的一堆operator>>
“成员函数”中的某一个,而operator
函数们可以缩写成运算符形式。至于能连续>>
,是因为cin
的operator>>
的返回值是对cin
的“引用”。以上“类”、“派生”、“重载”、“成员函数”、“引用”概念,大家先不必理会,下学期C++会讲。
istream
类还有其它一些成员函数,比如getline
。
cin.getline
的原型,按C的理解方式来写,有两个,是如下形状的(现在切莫去研究这个函数的实际原型!):
/// \brief cin.getline输入行
/// \param[out] _Str 存放行的数组
/// \param[in] _Count _Str数组的大小(包括结尾0)
/// \param[in] _Delim 如果有,那么表示把_Delim给的字符,而不是换行,当行结束符。
/// \return 其实是cin,不过其状态是被getline读掉一行之后
某奇怪类型 cin.getline(char *_Str, unsigned _Count);
某奇怪类型 cin.getline(char *_Str, unsigned _Count, char _Delim);
如果用了_Delim
,那么换行就不是结束符了,这是很有用的。
全局的getline
这个函数在<string>
头文件里声明,需要using namespace std;
(不要在.h里写这个)。按C的理解方式来写,原型也有两个,如下所示:
/// \brief getline输入行
/// \param[in] _Str 存放行的数组
/// \param[out] _Count _Str数组的大小(包括结尾0)
/// \param[in] _Delim 如果有,那么表示把_Delim给的字符,而不是换行,当行结束符。
/// \return 其实是cin,不过其状态是被getline读掉一行之后
某奇怪类型 getline(cin或某ifstream& _Istr, string变量& _Str);
某奇怪类型 getline(cin或某ifstream& _Istr, string变量& _Str, const char _Delim);
这里_Istr和_Str都是“引用”,所以函数可以改传过去的参数的值。用法如下:
string s;
getline(cin, s);