[College] C++字符串读入与进制转化-关于《实践教程》P10[程序]的一些总结

引子

在《实践教程》P10的程序代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>

using namespace std;

int main()
{
    char a[20];
    int i;

    cout<<"请输入以下包含空格的字符串:It's a book."<<endl;
    cin>>a[0]>>a[1]>>a[2]>>a[3]>>a[4]>>a[5]>>a[6]>>a[7]>>a[8]>>a[9];
    a[10]='\0';
    cout<<"保存的是:"<<a<<endl;
    cout<<"请重新输入一遍"<<endl;
    cin.get(a[0]);
    cin.get(a[1]);
    cin.get(a[2]);
    cin.get(a[3]);
    cin.get(a[4]);
    cin.get(a[5]);
    cin.get(a[6]);
    cin.get(a[7]);
    cin.get(a[8]);
    cin.get(a[9]);
    cin.get(a[10]);
    cin.get(a[11]);
    cin.get(a[12]);
    cin.get(a[13]);
    a[14]='\0';
    cout<<"保存的是:"<<endl;
    cout<<a<<endl;
    cout<<"注意:a[0]存放上次输入的回车符,a[14]存放本次输入结束的回车符,各自产生一个换行。"<<endl;

    cout<<"请输入一个八进制整数:0750"<<endl;
    cin>>i;
    cout<<"输出的数用十进制表示是:"<<endl;
    cout<<dec<<i<<endl;
    cout<<"请输入十六进制整数:0xff"<<endl;
    cin>>i;
    cout<<"输入的数用十进制表示是:"<<endl;
    cout<<dec<<i<<endl;

    cout<<"使用get.line()吸收0后面的字符:"<<endl;
    cin.getline(a,18);
    cout<<a<<endl;
    cout<<"改用显式指定类型,输入八进制数750:"<<endl;
    cin>>oct>>i;
    cout<<"输入的数用十进制表示是:"<<endl;
    cout<<i<<endl;
    cout<<"输入的数用八进制表示是:"<<endl;
    cout<<oct<<i<<endl;//指明用八进制输出以后一直有效
    cout<<"请输入十六进制整数:ff"<<endl;
    cin>>hex>>i;
    cout<<"输入的数用十进制表示是:"<<endl;
    cout<<dec<<i<<endl;//必须显式地改为十进制
    cout<<"输入的数用十六进制表示是:"<<endl;
    cout<<hex<<i<<endl;

    cout<<"请输入字符串:This is a cat."<<endl;
    cin.getline(a,18);
    cout<<a<<endl;
    cout<<"没有机会输入字符串:This is a cat。程序读了上次输入数字后的回车,输出了一个空串。现在可以重新输入一遍字符串:This is a cat。"<<endl;
    cin.getline(a,18);
    cout<<a<<endl;
    cout<<"成功!"<<endl;

    return 0;
} 

依照指示运行的输入输出如下:

请输入以下包含空格的字符串:It's a book.
It's a book.
保存的是:It'sabook.
请重新输入一遍
It's a book.
保存的是:

It's a book.

注意:a[0]存放上次输入的回车符,a[14]存放本次输入结束的回车符,各自产生一个换行。
请输入一个八进制整数:0750
0750
输出的数用十进制表示是:
750
请输入十六进制整数:0xff
0xff
输入的数用十进制表示是:
0
使用get.line()吸收0后面的字符:
xff
改用显式指定类型,输入八进制数750:
750
输入的数用十进制表示是:
488
输入的数用八进制表示是:
750
请输入十六进制整数:ff
ff
输入的数用十进制表示是:
255
输入的数用十六进制表示是:
ff
请输入字符串:This is a cat.

没有机会输入字符串:This is a cat。程序读了上次输入数字后的回车,输出了一个仅包含一个回车的空串。现在可以重新输入一遍字符串:This is a cat。
This is a cat.
This is a cat.
成功!

教材上的这段代码,实际上包含了两个类型的知识点:字符串读入进制控制

进制控制

在代码中,我见到了此前NOIP竞赛中没有见过的表达,即 oct,dec,hex。(可能是我太菜了。。。qwq

根据代码及其输出结果,我们可以猜测,它们都与进制转换有关——其实,看看下面这几个英文单词你就明白了:

octal 八进制的

hexadecimal 十六进制的

decimal 十进制的

在cin、cout语句中,我们通常用oct、hex、dec来声明输入输出数值的进制类型(若在程序中始终未声明,则默认为十进制读入输出):

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>

using namespace std;

int main()
{
    int i, j, k, l;
    cout<<"Input i(oct), j(hex), k(hex), l(dec):"<<endl;
    cin>>oct>>i; //输入为八进制数
    cin>>hex>>j; //输入为十六进制数
    cin>>k; //输入仍为十六进制数
    cin>>dec>>l; //输入为十进制数
    cout<<"hex:"<<"i="<<hex<<i<<endl;
    cout<<"dec:"<<"j="<<dec<<j<<'\t'<<"k="<<k<<endl;
    cout<<"oct:"<<"l="<<oct<<l;
    cout<<dec<<endl;
}

/*
Input i(oct), j(hex), k(hex), l(dec):
032 0x3f 0xa0 17
hex:i=1a
dec:j=63    k=160
oct:l=21
*/

值得注意的是,在cin、cout中指明进制后,该进制将会一直被默认为读入与输出的进制格式,直至下一次再次声明进制。

此外,这种进制控制方式只适用于整型,而对于实型、字符并不适用。所以想将小数转化为其它进制的同学还是老老实实码代码吧~

还有一个小细节:使用不带.h的头文件<iostream>时,必须在cin中指明数制,否则从键盘输入时,无法识别八进制和十六进制数开头的0和0x标志。指明后可省略0和0x标志。

字符串读入

比起上面这些比较陌生的进制控制,字符串读入对我而言显然要熟悉多了~(不由自主地想起了曾经被字符串各种支配的恐惧 T_T

在文章开头的程序中,用到了这么一些字符串的读入方式:

//第一种:逐个读入单个字符
cin>>a[0]>>a[1]>>a[2];
//第二种:逐个读入单个字符串(它与前一种的区别我们之后会提到)
cin.get(a[0]);
cin.get(a[1]);
cin.get(a[2]);
//第三种:一次性读入整个字符串
cin.getline(a,4);

下面,我们来逐个认识这三种读入方式:

第一种:cin

在程序中,是用cin逐个读入单个字符,但cin不能读取空格、回车、tab键,并且将其作为单次输入的结束。因此,程序使用这种方式读取时,字符串中的空格不能被存储,而是被当作了输入的字符间的间隔。

<一点点补充> cin除了可以逐个读入单个字符,还可以读入字符串——类似的,这个字符串不能包含空格、tab、回车。例如下面这段代码:

char s[30];
cin>>s;
cout<<s<<endl;

倘若输入"I love you.",则输出结果为"I",因为cin读入字符串遇到字符‘I’后的空格时自动结束,s中只存储了空格前的字符。

综上所述,cin只能被用来读入连续的纯字符串,倘若字符串中可能出现空格、回车、tab,还是换一种读入方式吧!

第二种:cin.get()

cin不能读取空格,那么若我们想要读入一个包含空格的字符串怎么办呢?这个时候我们就可以用cin.get()啦!cin.get()不仅可以存储普通字符,还能够存储空格、回车、tab!但在程序中,我们会发现使用cin.get()存储的字符串前后分别含有一个换行符。那么,它们是怎么产生的呢?

下面,我们来简单引入一下“键盘缓冲区”这个概念:对于cin、cin.get()、cin.getline()来说,它们总是首先从键盘缓冲区读入数据,若键盘缓冲区有数据,就直接读取,若无数据,就等待用户输入。

我们之前使用cin.get()存储字符串,其中代码cin.get(a[0])存储的实际上就是上一次输入字符串后我们键入的回车符,这个回车符本来被存放于键盘缓冲区,在执行cin.get(a[0])时被存储在了a[0]中。而在字符串后的回车则是我们本次输入结束时的回车。

第三种:cin.getline()

与cin.get()不大相同,cin.getline()是一次性读入整行字符串。cin.getline()可以完美地读入空格与制表符,但当其读入换行符时,它会结束字符串的读入。对于文章开头的程序,第一个cin.getline()仅仅读入了一个换行符,因此在输出时只输出了一个空串。

cin.getline()的格式为:cin.getline(字符串名,字符串长度)。值得注意的是,cin.getline()读入字符串后会在末尾自动添加一个空字符‘\0’,作为字符串结尾的标志。同时需要注意的是,我们在cin.getline中输入字符串长度时,也需要考虑'\0'的存在!

你有可能已经发现了,在程序使用cin.get()读入字符串时,也在最后手动将字符串的后一个位置赋值为'\0'。我们不妨将这行语句删除,看看会发生什么——

//本次字符串读入的输出结果:
It's a book.
?u發A€3u

我们发现,若删除此语句,则输出的字符串a后出现乱码。原因不难理解,cout输出字符串a时,将'\0'作为字符串结束的标志,当输出'\0'时停止,若无'\0'则继续输出后面的元素,而在例子中后面的元素未被赋值,其值是不确定的,导致出现乱码。

<一点点补充> 在例子中,我们多次遇到了读入字符串时“意外”读入换行符导致输入提前结束的情况。那么如何避免呢?通常情况下有两种方法:

1> 第一种,在字符串读入前加上cin.get(),也就是说,我们可以通过在字符串读入前先单独读入留在键盘缓冲区的换行符,来避免在读入字符串时读入它。

2> 另一种方法是使用cin.ignore(),cin.ignore()函数中有两个参数,分别为数值型的a和字符型的ch ,即cin.ignore( a, ch )。它表示从输入流 cin 中提取字符,提取的字符被忽略,不被使用。而每抛弃一个字符,它都要进行计数和比较字符:如果计数值达到 a 或者被抛弃的字符是 ch ,则cin.ignore() 函数执行终止;否则,它继续等待。它的一个常用功能就是用来清除以回车结束的键盘缓冲区的内容,消除上一次输入对下一次输入的影响。例如可以这么用,cin.ignore(1024, '\n')。我们通常把第一个参数设置得足够大,这样是为了只让第二个参数 '\n' 起作用,所以这一个语句的目的就是把回车符(包括回车符本身)之前的所有字符从输入缓冲流中清除出去。

3> 其实还有一些简单粗暴地清除键盘缓冲区的办法,例如Windows系统下的fflush(stdin)与rewind(stdin)(它们都被包含在头文件<cstdio>中),在Linux系统下的setbuf(stdin,NULL)。

 

posted @ 2019-09-26 12:25  SinGuLaRiTy2001  阅读(296)  评论(0编辑  收藏  举报