一、引例 – 牛客网OJ题

为了更好地说明这个问题,我们以一道牛客网的题目作引例。题目链接贴在这里

判断是元音还是辅音_牛客题霸_牛客网KiKi开始学习英文字母,BoBo老师告诉他,有五个字母A(a), E(e), I(i),。题目来自【牛客题霸】https://www.nowcoder.com/practice/7eb4df4d52c44d309081509cf52ecbc4?tpId=290&tqId=307549&ru=%2Fpractice%2F9cc35bd0754f4feca18e10e57c672467&qru=%2Fta%2Fbeginner-programmers%2Fquestion-ranking&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E8%25AF%25AD%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D290

题干如下:

基本思路:

这道题的基本思路很明确,将所有的元音字母作为元素定义一个字符数组arr[5],这时有人会说,不对呀,这不严谨,算上大小写应该是十个呀,为什么是arr[5]。是的,言之有理,不过这个的解决可以在if 语句上做文章,不妨设获取的变量为ch,if ( ch==arr[i] || ch==arr[i]-32),因为在ASCII码中,大小写字母在数值上相差32,这样我们就不必创建另外五个的空间,也会减少循环的长度,既减少时间复杂,又减少空间复杂,何乐而不为呢” />#includeint main(){int i=0;char arr[5]={‘a’,’o’,’e’,’i’,’u’};char ch;for (i = 0; i < 5; i++) {if (ch == arr[i] || ch == arr[i] – 32){printf("Vowel\n");break;}}if (i==5)printf("Consonant\n");return 0;}

可是真的是这样吗,让我们看一看这个运行的结果。

为什么会这样呢?好像和我们预想的不太一样。从结果上看,我们好像完成了对一个字母的判断,那么我们该如何多组输入呢?这里就会用到while((scanf(“%c”,&ch))!=EOF)来完成多组输入。

接下来让我详细了解认识一下即将在题目里大展身手这位“大家伙”,同诸位观一观他的庐山真面目。

while((scanf(“%c”,&ch))!=EOF)

首先我们来认识一下scanf( )的返回值。要scanf的返回值等于成功输入的个数。

如果成功,该函数返回成功匹配和赋值的个数。

如果达到文件末尾或发生读取错误,则返回EOF

那么EOF又是何方神圣呢?

其实答案很简单,EOF是 End Of File的缩写,即表示文件的结束,他的宏定义值为-1

在文本中,数据通常以ASCII码值的形式存放,但ASCII码值的范围为0-127,不可能出现-1,因此可以用EOF作为文件的结束标志

那么,有没有其他的方法呢,其实基于此,我们仍然可以做一些小小的变动,让他变得更加简洁些,该怎么做呢?当然我们你还要回到数据在计算机的储存方式——二进制。

while(~(scanf(“%c”,&ch)))

当读取失败时,scanf( )返回-1,经过取反会变成 0,跳出循环,完成while((scanf(“%c”,&ch))!=EOF)一样的效果。

知道了如何多组输入,我们再回到题目,我们根据以上做一个小变动再试一次把!

#include int main() {int i = 0;int arr[5] = {'a', 'e', 'i', 'o', 'u'};char ch;while ((scanf("%c", &ch))!=EOF ) {for (i = 0; i < 5; i++) {if (ch == arr[i] || ch == arr[i] - 32){printf("Vowel\n");break;}}if (i==5)printf("Consonant\n");}return 0;}

哎” />

我们只输入了两个字符为什么会出现三个打印结果呢?要理解为什么,我们要对scanf( )的理解要更加深刻一些。不妨,让我们了解一下,输入函数是如何工作的。

当我们从控制台输入字符时,scanf( )并没有全部接收,而是将他们存放在寄存器中按照输入的顺序一个一个读取,同时要知道,在输入时的 “回车”,也属于字符,最终会被scanf( )读取。因此我们需要把“回车”去除干净。

利用getchar( )去除换行符

getchar( )和scanf( )都属于输入函数,不妨,让我们了解一下,输入函数读取字符的原理。接下来,让我们认识一下,缓冲区。

缓冲区

缓冲区是内存空间的一部分,也就是说在内存空间中预留了一定大小的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

工作原理

当调用输入函数scanf( )时,输入函数会将我们输入的数字,输入到输入缓冲区,而当我们的“输入缓冲区”有内容时,再次输入将不会被执行,而是直接跳过执行,将输入缓冲区的内容赋给变量。也就是说,在读取了前一个字符以后,换行符留在了缓存区,使得后面的字符无法赋值,

这时getchar( )的使用,将换行符提前读取。这样,scanf( )就可以顺利读取后面的内容,最终完成任务。

#include int main() {int i = 0;int arr[5] = {'a',for (i = 0; i < 5; i++) {char ch;while ((scanf("%c", &ch))!=EOF ) {getchar();for (i = 0; i < 5; i++) {if (ch == arr[i] || ch == arr[i] - 32){printf("Vowel\n");break;}}if (i==5)printf("Consonant\n");}return 0;}

那么可不可以不使用getchar( )呢?不使用的话,最好让scanf( )自己读取字符的时候同时把换行符一并读取了,这样的方法能行吗?其实,答案不容置喙,是肯定的。

scanf(“%c\n”,&ch)同样可以去除换行符

三、练手

好了,了解到上面的知识,相信我们对使用 while 语句的多组输入,有了更深的认识。不妨,我们再做一道题练练手。

我们以一道牛客网的题目作练手。题目链接贴在这里

字符逆序__牛客网字符逆序https://www.nowcoder.com/questionTerminal/cc57022cb4194697ac30bcb566aeb47b

题干如下

//错误答案模板#include #includeint main() {char str[10001];while((scanf("%s",str))!=EOF){ int len=strlen(str); int left=0; int right=len-1; while(left<right) {char tmp=str[left];str[left]=str[right];str[right]=tmp;left++;right--; } puts(str);}return 0;}

基本思路

这道题的基本思路还不简单,利用多组输入,将所有字符全部存放在数组,接着用双下标法完成数组逆置,然后用puts( )函数将数组元素输出。哎呀,简单简单,下一个。可是答案真的仅仅如此吗?当我们信誓旦旦的点击“保存并提交”时,结果会发现,事实上远没有我们想的那么简单。

究竟是为什么呢? 当我们看到实际输出的时候可能会非常诧异。怎么会实际输出是两行呢?这主要是scanf( )的原因,scanf( )当遇到空格会读取结束。由于这样的特性,实际输出一分为二分别输出。那么这个问题该怎么解决呢?于是我么想到了scanf( )的正则表达。

scanf( )正则表达的多组输入

while((scanf(“%[^\n]”,str))!=0)

那么问题来了,为什么scanf( )正则之后,停止条件变成了scanf(“%[^\n]”,str))!=0呢?其实答案很简单,当使用正则之后,scanf( )的返回值变成了成功读取字符串的个数,不再读取则返回0作为返回值。

那么除了正则表达,还可以使用什么进行代替呢?于是,我们想到了——gets( )函数。

使用gets( )的多组输入

不妨,我们先了解一下gets( )函数。

gets( )函数

gets( )函数的原型:

char * gets(char *string)

gets函数作用:

从标准输入中读取字符,遇到换行符'\n'(newline)

gets函数返回:

读取成功时返回一个指针,指向的是存放读取数据的地址值;读取失败(遇到EOF或者其他错误)时,返回NULL;

当gets( )函数读取失败返回时,返回NULL。由此,我们可以知道gets函数的多组输入,是while(gets(arr)!=NULL)