注:本文章类之间关系,以及相关函数均参考cplusplus
官方网站,网站链接点击这里。
各种输入输出流类之间的继承关系
从上图可以看出来,我们平时包含的头文件#include
,其实是继承自istream
,ios
,ios_base
等类方法。此文只分析最常用的输入输出函数get
,getline
,包括get
,getline
普通函数和get
,getline
类成员方法。
注:本文中,对于普通函数(即不属于任何一个类的函数)统称为普通函数,对于类成员函数(即类内的成员函数)统称为类方法。
对于get
,getline
既有类方法
又有普通函数
,下面我们依次分析get
,getline
的类方法
和普通函数
。
先看一下get类方法有哪些
即有如下方法:
int get(); // 1
istream& get(char& c); // 2
istream & get(char* s, streamsize n); // 3
istream& get(char* s, streamsize n, char delim); // 4
istream& get(streambuf& sb); // 5
istream& get(streambuf& sb, char delim); // 6
对于上述类方法1
,不接受任何参数,即可以通过cin.get()
来调用,且可以看出该函数的返回值为int
,这表示从输入缓冲区中读取一个字符时,返回的是该字符对应的ASCLL码
的值。我们可以使用一下两种方法来接收cin.get()
的返回值。
int ch = cin.get(); // 1.1
char ch2 = cin.get();//1.2
对于1.1
来说,ch
即为接收字符的ASCLL码
的值,对于1.2
来说,ch2
即为该ASCLL码
对应的字符。可能有同学会有疑问,cin.get()
的返回值不是int
类型吗,为什么可以有char ch2 = cin.get()
这种赋值?这其实是因为中间发生了隐式强制类型转换,上式1.2
等价与下面的语句:
char ch2 = (char)cin.get();
从int
到char
的转换为什么可行,我们可以通过下面的一条语句确认一下:
char tmp = (char)97;//97 ASCLL 码对应字符 ‘a’
即如果tmp
能正确输出字符a
,则说明1.2
的转换可以发生,测试代码如下:
void testGetMethod1() {
char ch = cin.get();
cin.get();//读取掉缓冲区中的换行符 1.1.1
char ch2 = cin.get();
cin.get();//读取掉缓冲区中的换行符 1.1.2
char tmp = (char) 97;
cout << "ch:[" << ch << "] ch2:[" << ch2 << "] tmp:[" << tmp << "]" << endl;
}
输出结果:
可能有疑惑为什么代码中间会有1.1.1和1.1.2
的cin.get()
,这主要是因为我们在输入一个字符并按下回车后才会刷入到输入缓冲区并读取,因为cin.get()
一次只能读取一个字符,故我们在输入一个字符并按下回车后相当于是输入了两个字符(一个是我们明确输入的字符,一个是回车),故我们需要将缓冲区中的换行符读走。
对于类方法2
,该方法接收一个char形参数,并且返回一个istream对象的引用,故使用cin.get(char&)
时我们可以拼接使用。使用方法如下:
void testGetMethod2() {
char ch;
char ch2;
int i;
//普通读取,不拼接
cin.get(ch);
// cin.get(i); //注意此时转换不可行,只能接收一个char形的变量
cin.get();//读取掉缓冲区中的换行符
cout << "first read ch:[" << ch << "]" << endl;
//拼接读取
cin.get(ch).get(ch2);
cin.get();//读取掉缓冲区中的换行符
cout << "second read ch:[" << ch << "] ch2:[" << ch2 << "]" << endl;
}
输出结果:
对于类方法3
,我们看到有两个参数,一个是char*
,另一个是streamsize
,对于第一个参数,其实是我们准备存放从输入缓冲区中读取的字符的地址,n
表示我们准备存放的缓冲区的大小。实际上streamsize
的类型是long long int
,从如下可以看到:
故对于istream& get(char* s, streamsize n)
,我们需要预先分配一个缓冲区用来存放数据,具体使用方法如下,我们测试如下实现:
注:这里所有的输出测试,只是针对第一次输入的情况描述的
输入小于10个字符不包含空格,观察输出
void testGetMethod3() {
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));//输入小于10个字符不包含空格,观察输出
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
}
输出如下:
可以看到afterBufter字符为空,这是因为,cin.get(char*,streamsize)
方法读到换行符结束,并且把换行符留在缓冲区中;因为第一次调用cin.get(char*,streamsize)
时把换行符留在了缓冲区,故第二次调用cin.get(char*,streamsize)
时,读取到换行符直接结束,故afterBuf
为空。
输入小于10个字符不包含空格,观察输出
void testGetMethod3() {
#if 0
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));//输入大于10个字符不包含空格,观察输出
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#else
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));//输入大于10个字符不包含空格,观察输出
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get();//读走缓冲区的换行符
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#endif
}
输出如下:
可以看到正常输出。
输入小于10个字符包含空格,观察输出
void testGetMethod3() {
#if 0
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));//输入大于10个字符不包含空格,观察输出
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#else
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));//输入大于10个字符不包含空格,观察输出
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get();//读走缓冲区的换行符
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#endif
}
从上面输出结果可以看出,遇到空格时cin.get(char*,streamsize)
并不会停止,而是会继续读取,故cin.get(char*, streamsize)
是读取一行数据。
输入大于10个字符
void testGetMethod3() {
#if 0
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#else
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get();//读走缓冲区的换行符
cin.get(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#endif
}
从结果可以看出buf
只读走了其指定大小 - 1个字符,然后剩余的字符留在缓冲区中,由于缓冲区中有数据,故接下来的cin.get()
先从缓冲区中读走一个字符,然后cin.get(afterBuf, sizeof(afterBuf))
接着读取剩下的内容,故有如上的输出结果。
对于类方法4
,和方法3
类似,唯一的区别就是方法4
指定了读取字符串时的结束符为这里指定的delim
字符,方法3
是默认\n
为结束符,测试如下:
void testGetMethod4() {
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.get(buf, sizeof(buf), '#');
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.get();//读走缓冲区的换行符
cin.get(afterBuf, sizeof(afterBuf), '#');
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
}
输出结果:
开始时,输入字符afdda
后按下回车发现还是让继续输入,紧接着输入a#dfddf
按下回车后才输入buf
,并且buf
为afdda\na
,即buf
中间有个换行符,从输出结果可以看出。紧接着我们调用了cin.get()
读走了一个字符;因为cin.get(char*,streamsize)
和cin.get(char*,streamsize,char)
都会把结束符留在缓冲区中,故cin.get()
读走的实际上是#
,因此接下来的cin.get(afterBuf,sizeof(afterBuf), '#')
就是读取到字符串dfddf\nfadf
,从输出可以看出,此时缓冲区还留有字符#fadsf
。
对于类方法5和类方法6
,我们看到有定义streambuf
,该类型实际为
即实际上对应的是一个类,因为这两个不太常用,本文不做分析。
istream& getline(char* s, streamsize n); // 7
istream& getline(char* s, streamsize n, char delim); // 8
对于类方法7
,其实和get
方法的类方法3
类似,区别就是类方法7
读取到结尾(默认是换行符)时,丢弃该换行符,而类方法3
则是将结尾标志(默认是换行符)保存在输入缓冲区中,测试如下:
输入小于10个字符
void testGetMethod7() {
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.getline(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
// cin.get();//读走缓冲区的换行符
cin.getline(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
}
从结果可以看到,我们不需要额外的cin.get()
来讲结束符读走,因为getline已经帮我们读走结束符并丢弃
输入大于10个字符
void testGetMethod7() {
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.getline(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
// cin.get();//读走缓冲区的换行符
cin.getline(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
}
我们发现了奇怪的现象,afterBuf
为什么为空?这其实是因为cin.getline
在读取时会有下面的准侧:
\0'
。cin.getline读取字符时读取够指定字符或者遇到结束符结束
cin.getline
如果读取到了指定的字符,且接下来的不是换行符,会设置failbit
位,即读取失败,如果后面的想正常读取,必须要清空failbit
位,即需要先调用cin.clear()
。cin.getline
读取字符时,如果给定的缓冲区只有10个字节时,当读取9个字节时,如果接下来的是结束符
(这里默认是换行符),则读取该换行符并丢弃,此时并不会设置failbit
位,如果接下来的字符不是结束符
,则会设置failbit
位。因此上述afterBuf
读取出来是空字符串。正常读取,我们看如下代码:
void testGetMethod7() {
#if 0
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.getline(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
// cin.get();//读走缓冲区的换行符
cin.getline(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#else
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.getline(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
cin.clear();
cin.getline(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
#endif
}
输出结果
不包括换行符输入9个字符
void testGetMethod7() {
char buf[10];
char afterBuf[100];
memset(buf, 0, sizeof(buf));
memset(buf, 0, sizeof(afterBuf));
cin.getline(buf, sizeof(buf));
cout << "less 10 character no space :buf [" << buf << "]" << endl;
// cin.get();//读走缓冲区的换行符
cin.getline(afterBuf, sizeof(afterBuf));
cout << "less 10 character no space :afterBuf [" << afterBuf << "]" << endl;
}
输出结果:
从上可以更好的理解列方法getline
的读取字符的准侧。
类方法8和上面基本一样,只是指定了读取结束符,这里不再分析。
类方法get(这里指的是类方法3 和类方法4)与类方法getline对比
get
方法将结束符保存在缓冲区中,而getline
读取结束符并丢弃类方法get
的缓冲区大小,类方法get
也不设置failbit
位,可是此种情况下类方法getline
会设置failbit
位。类方法get
和类方法getline
都是读取一行数据。.....写了挺久了,后面有空再把非类方法的getline和get给补上。
如果有帮助,请一键三连呀,多谢支持~~~