最近遇到一个bug怎么都调不出来,后来在网上查了查是cin的使用出了问题,还是对c++理解太浅了

于是来写一篇blog记录一下,方便以后查找。


cin的常用方法

std::cin位于头文件<iostream>中

cin最常见的用法便是直接用>>运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int main()
{
int a;
cin >> a;
char c;
cin >> c;
/*
cin >> c;会过滤掉不可见字符
若不想过滤则可以使用cin >> noskipws >> c;
*/
string s;
double f;
cin >> s >> f;
return 0;
}

多数人对cin的使用仅限于此,而若先输入一个字符就会发现程序后面的cin全部失效,具体的原因下面会讲。

和函数不同,cin的本质可以看成一个有重载运算符的struct,名称为istream。这也解释了诸如cin >> a >> b的这种形式,可以理解为(cin >> a) >> b,而istream与int变量使用>>进行运算返回cin本身。


cin.get()

此函数有四种形式,分别为

1
2
3
4
5
6
7
8
9
10
11
int cin.get();
//读取一个字符,以int形式返回
istream& cin.get(char& v);
//读取一个字符并存储到v中
istream& get(char* s, streamsize n);
//向s中读入长度上限为n-1的字符串并在末尾添加'\0',接受空格,遇到换行或EOF自动停止
istream& get(char* s, streamsize n, char delim);
//同上,参数中新增一个终止字符delim,在遇到此字符时也停止

//使用cin.get()读取字符串时遇到的换行等终止字符依然留在缓冲区内
//可以看到,我们能使用cin.get(c1).get(c2) >> a;这样的链式编程形式

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

int main()
{
char c, s[100];
c = cin.get();
cout << c << endl;
cin.get(c);
cout << (int)c << endl;
cin.get(s, 5);
cout << s << endl;
cin.get(s, 50);
cout << s << endl;
cin.get(c);
cout << (int)c << endl;
return 0;
}
/*
输入:
a
12345678

*/
/*
输出:
a
10
1234
5678
10

*/

cin.getline()&getline()

cin.getline()用法类似于cin.get()的读取字符串:

1
2
istream& getline(char* s, streamsize count);
istream& getline(char* s, streamsize count, char delim);

与cin.get()不同,cin.getline()不会将换行等终止字符留在缓冲区中。

而getline()在头文件<string>中,用于对string读入一整行,用法与cin.getline()类似:

1
2
3
istream& getline(istream& is, string& str);
istream& getline(istream& is, string& str, char delim);
//通常is项传入cin

类似地,也可以有getline(cin, s1) >> s2;的操作。

另外早期c语言中有gets()函数对char*输入一行,而现在被弃用,需要使用fgets(),这里不做展开。


cin的其他用法

  • cin.ignore()
1
2
istream& ignore(streamsize n = 1, int delim = EOF);
//忽略缓冲区中n个字符
  • cin.peek()
1
2
int peek();
//返回当前指针位置处的字符
  • cin.putback()
1
2
3
istream& putback(char c);
//当前指针位置插入字符c
//最多只能同时插入一个字符,若想再次插入字符则需将前一个字符取出
  • cin的条件状态

    在使用cin对数字输入字符时会出现错误,此时cin内部会设置条件状态,有以下四种状态:

    1
    2
    3
    4
    5
    ios_base::goodbit//无错误
    ios_base::eofbit//已到达文件尾
    ios_base::failbit//非致命错误,可挽回
    ios_base::badbit//致命错误,无法挽回
    //通常值分别为0,1,2,4

    对于的成员函数如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    bool eof();
    //若流中eofbit置位,则返回true
    bool fail();
    //若流中failbit置位,则返回true
    bool bad();
    //若流中badbit置位,则返回true
    bool good();
    //若流中goodbit置位,则返回true
    void clear(istream::iostate flags = ios_base::goodbit);
    //将给定的标志位flags置为1,其他位清空。默认为goodbit
    int sync();
    //清空缓冲区,只能在goodbit时使用。成功返回0,失败返回-1且置位badbit。
    //若绑定了输出流会同时刷新输出缓冲区
    void setstate(istream::iostate flags);
    //将对应的条件状态位置为1
    istream::iostate rdstate();
    //返回当前条件状态,istream::iostare为与系统相关的整数类型

    一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #include <iostream>
    using namespace std;

    int main()
    {
    //freopen("in.txt", "r", stdin);
    int a;
    cin >> a;
    cout << cin.rdstate() << ' ' << cin.good() << endl;
    cin.clear();
    cout << cin.rdstate() << ' ' << cin.good() << endl;
    char c;
    cin >> c >> a;
    cout << c << ' ' << a << endl;
    return 0;
    }
    /*
    输入:
    a 1
    */
    /*
    输出:
    4 0
    0 1
    a 1
    */

常见的将cin置于条件判断中,如while(cin>>a)则是判断goodbit位。

最后,在代码中必须清楚cin的原理,不能随意使用。