C++输入输出流解析

注:本文章类之间关系,以及相关函数均参考cplusplus官方网站,网站链接点击这里。


各种输入输出流类之间的继承关系
在这里插入图片描述
从上图可以看出来,我们平时包含的头文件#include <iostream>,其实是继承自istreamiosios_base等类方法。此文只分析最常用的输入输出函数getgetline,包括getgetline普通函数和getgetline类成员方法。

注:本文中,对于普通函数(即不属于任何一个类的函数)统称为普通函数,对于类成员函数(即类内的成员函数)统称为类方法。

对于getgetline既有类方法又有普通函数,下面我们依次分析getgetline类方法普通函数

get类方法

先看一下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

cin.get()

对于上述类方法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();

intchar的转换为什么可行,我们可以通过下面的一条语句确认一下:

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.2cin.get() ,这主要是因为我们在输入一个字符并按下回车后才会刷入到输入缓冲区并读取,因为cin.get()一次只能读取一个字符,故我们在输入一个字符并按下回车后相当于是输入了两个字符(一个是我们明确输入的字符,一个是回车),故我们需要将缓冲区中的换行符读走。

cin.get(char&)

对于类方法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;
}

输出结果:
在这里插入图片描述

istream& get(char* s, streamsize n)

对于类方法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))接着读取剩下的内容,故有如上的输出结果。

istream& get(char* s, streamsize n, char delim)

对于类方法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,并且bufafdda\na,即buf中间有个换行符,从输出结果可以看出。紧接着我们调用了cin.get()读走了一个字符;因为cin.get(char*,streamsize)cin.get(char*,streamsize,char)都会把结束符留在缓冲区中,故cin.get()读走的实际上是#,因此接下来的cin.get(afterBuf,sizeof(afterBuf), '#')就是读取到字符串dfddf\nfadf,从输出可以看出,此时缓冲区还留有字符#fadsf

istream& get(streambuf& sb)和istream& get(streambuf& sb, char delim);

对于类方法5和类方法6,我们看到有定义streambuf,该类型实际为
在这里插入图片描述在这里插入图片描述即实际上对应的是一个类,因为这两个不太常用,本文不做分析。

getline类方法

在这里插入图片描述

istream& getline(char* s, streamsize n); // 7
istream& getline(char* s, streamsize n, char delim); // 8

istream& getline(char* s, streamsize n)

对于类方法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在读取时会有下面的准侧:

  • 读取数据字符时不能超过给定缓冲区的大小 - 1,即如果缓冲区是10,那么我最多只能读取9个字符,因为要留一个字节放’\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给补上。

如果有帮助,请一键三连呀,多谢支持~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug.Remove()

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值