“字符串+数组”类题目的知识盲区照明和解题技巧讲解(上):字符字符串的输入问题与相关函数的运用技巧

目录

一、前言

二、字符与字符串的输入问题

1、缓冲区

2、字符的读取

3、字符串的读取:

三、相关函数的使用技巧

1、strlen

2、strcmp

3、strcpy

4、strcat

5、补充


一、前言

  相信有许多人,在初学C语言进入数组一章时,会感到题目骤然变难,常常摸不着头脑,感觉一些知识似学非学,想要代码实现就更棘手了。其实这很“情有可原”。

一般学校教材在讲数组时,不仅仅讲整形数组,同时还讲字符串数组。不仅仅是一维数组,还有多维数组。“整形”与“一维”比较好理解,但“字符串”+“多维”就难起来了。

一是字符串的相关知识对大部分初学者来说相对陌生,二来要想掌握二维数组,首先对使用一维就要求有很高的熟练度,可这章的知识仿佛一口气全说了出来,没有给初学的大家一个友好的喘息时间。

而笔者自学所用的教材(这里也跟大家分享一下^)

《明解C语言(入门篇)》(后有中级篇) 

这本书在讲解顺序上跟学校教材不同,我们看一下目录:

 在初学数组时,它的整个章节都限定在整形数组,并没有涉及到字符串数组,而在学完一些简单思想(比如冒泡排序,递归调用)和一些基础知识(EOF、输入字符串)之后(第8章),才单独拿出一章来介绍字符串的基本知识和字符串数组的补充(第9章)。相当来讲,节奏更加缓和,顺序也更友好。所以别灰心,课上听不懂,题目做不出不是你笨,是因为学的顺序有所不同。

现在,我们抛开这些不谈,具体的来看看“字符串+数组”的一些基本知识和解题技巧,希望大家看完这期,能对相关知识和题目不再感到模糊、惧怕。

二、字符与字符串的输入问题

在指挥计算机解决字符串问题之前,首先要让它知道有哪些字符串,也就是输入。

我们都知道,关于输入的函数主要有:scanf、gets、getchar

那么它们之间有什么区别呢?我们来区分一下:

函数作用
scanf

可以输入字符和不含“空格与回车”的字符串;

输入空格或回车时,清空缓冲区。

gets

可以输入字符、字符串;

只在遇到回车时,清空缓冲区。

getchar

只有输入字符的功能;

当输入字符后,该字符即赋给了()里的变量。

若()里为空,则吞掉输入的字符。

对于上表,需要补充解释多个问题:

1、缓冲区

内存空间的一部分,当我们输入一些内容时,并不是直接存储进计算机的,而是暂时保存在缓冲区内,当计算机识别到它认可的结束符时(比如对于scanf的空格回车,gets的回车),它才会将缓冲区的内容先保存,再清空这个空间。

2、字符的读取

对于上面三种输入函数,都可以用来输入字符。但是字符有些特殊,当我们一输入字符时,该字符就直接保存了,而当我们再回车的时候,在有些情况下,该回车就会被当成字符进入下一次的输入,导致错误。对于许多初学者来说,这个问题显得特别棘手,做题时常常是思路都对,就被输入卡住,判题系统不让过。笔者也为此“坐了很多次牢”。

我们来重点分析一下:

1)例一:

#include<stdio.h>
int main()
{
	int i;
	char a[3] = { 0 };

	for (i = 0; i < 3; i++)
		scanf("%c", &a[i]);

	for (i = 0; i < 3; i++)
		printf("%c", a[i]);
	
	return 0;
}

设一个字符数组,需要将“a,b,c”输入进去,然后分别打印。

如果我们的操作时:a-> \n -> b -> \n -> c -> \n

结果会是“abc”吗?(注意循环输出没有换行符,所以理想结果是三个字符连在一起的)

其实大家亲手试一试就会发现不能实现,甚至输入c的机会都没给

 当我们输入a,回车,b,回车,程序就已经进入输出环节了。这正好印证了我们提到的——回车可能会被下一次的输入误读取。所以相当于我们输入的三个字符其实是“a,\n,b”,而第二个回车起的是结束输入程序的作用。在之后的输出环节里,输出a,接着当计算机识别到输出的是转义字符时,就进行了换行操作,最后输出b。

那么如何解决这个问题?

利用“ %c”和getchar()

先说getchar(),我们之前提到getchar后括号里没有变量,则会吞掉输入的一个字符。给代码加上这一句:

 我们发现过程和结果就都正确了,

 因为我们回车进行换行操作后,getchar()正好就吞掉了这个已经没有作用的回车,使得我们后续的输入不被它影响,之后的每一次输入字符加回车,都进行该操作,保证了程序的正确。

至于scanf(" %c"),在%c前加一个空格也是同样的道理,我们可以理解为这个空格也有吞缓存区剩余的单个字符的功能。当缓存区里有空格或回车,它就能跟其抵消,使不影响后续输入。如果没有,不影响输入。

2)例二:

#include<stdio.h>
int main()
{
	char ch;
	char a[20];

	scanf("%s", a);
	scanf("%c", &ch);

	printf("%s\n", a);
	printf("%c", ch);
	return 0;
}

要求输入字符串+字符,并输出它们。比如输入qwer和A。

我们的操作如果是"qwer+回车+A“的话,会发现运行结果为:

 也没有给输入A的机会,这是因为字符串后的回车被读取了,赋给了变量ch,抢了“A”的位置。这个例子和第一个的原理相同,大家可以自己试着更正。

如果输入顺序反过来呢?

3、字符串的读取:

3)例三:

 先输入字符再字符串。我们试着输入字符A+回车+qwer。

 发现字符串的读取并没有受到回车的影响,甚至在输入A之后输入几十个回车,几十个空格,都不会影响%s的读取。这是为什么呢?我们这样去解释:在scanf中,对于字符串(非单个字符)的读取,是不接收空格和回车的。空格回车都是空字符,当scanf读取了它所需要的内容后,如果识别到空字符,才会终止。

对于例三,当scanf读取到A以后,它是还在等字符串的输入的,所以之间的空字符想让它终止它都不认,直到输入字符串后碰到空字符,它才罢休。 

4)例四:我们再对输入进行小小变动。

#include<stdio.h>
int main()
{
	char ch;
	char a[20];

	scanf("%c", &ch);
	gets(a);

	printf("%c\n", ch);
	printf("%s", a);

	return 0;
}

同样要求输出A和qwer,输入以回车做分界,结果如下:

观察程序,不过是将scanf换成了gets,但执行起来就连输入字符串qwer的机会都没给。这是因为:gets识别到回车便终止。

这里补充补充重点:用gets函数读入字符串时,是可以将空格读进去的。这也就使得gets的使用范围比scanf更广。比如我们要将“EDG will defeat DK in LOL's finals” 当做字符串输入一个数组里,scanf就不能胜任,因为当遇到空格时,它会觉得EDG就是字符串的全部,它已经读取了它想要的,又遇到了空字符,所以提前终止了。而gets就能完美读取,直到输入finals后回车终止。

好了,我们整整用了四个例子,详细地分析了有关字符、字符串的输入问题,下面再总结一下容易犯错的点:

1、对于字符的输入(%c),在scanf中是能读取空字符的,一输入就直接读取了,如果想用空格间隔,分别输入多个字符,需要运用“ %c”和getchar(),吞掉缓存区多余的空格,使不影响后续读取。

2、对于用scanf输入字符串,是不能读取空字符的,scanf只在它所需要的内容都读取到后,识别空字符才会终止。

3、对于用gets输入字符串,能读取空格,以回车终止,使用范围比scanf更广。

三、相关函数的使用技巧

下面我们再谈谈<string.h>里常用的一些函数。

函数联想作用
strlen(a)length(长度)返回字符串a的长度。
strcmp(a,b)compare(比较)比较a,b的大小,如果a大,返回正值;如果b大,返回负值;如果a=b,返回0。
strcpy(a,b)copy(拷贝,复制)将字符串b复制给a,覆盖a。
strcat(a,b)cat不是“猫”哈,是concatenate(连接)将字符串b连接到a的后面。

1、strlen

这大概是最常用的函数了,我们用一道题来找到它的用武之地:

对于一个字符串,我们往往是将其看成一个个字符连接在一起,所以常用字符数组来储存它。然后通过遍历数组的每一个元素看是否满足条件。那么如何确定输入的字符有多长呢?或说,何处作为遍历的终点呢?

如果不运用strlen,我们也能做出该题:

#include<stdio.h>
int main()
{
	int i,j=0;
	char a[80] = { 0 };
	char vowels[80] = {0};
	gets_s(a);

	for (i = 0; i < 80; i++)
	{
		if (a[i] == '\0')//当遇到\0,说明字符串到了末尾。
			break;
		if (a[i] == 'a' || a[i] == 'e' || a[i] == 'i' || a[i] == 'o' || a[i] == 'u')
			vowels[j++] = a[i];
	}

	printf("%s", vowels);
	return 0;
}

当识别到了\0,我们运用了if判断条件跳出来循环,避免了后续无效遍历。

如果运用strlen怎么做呢?

 strlen(a)返回的值就等于字符串的长度,而字符数组的元素个数正好等于它,所以正好表示的是从a[0]遍历到a[strlen(a)-1],共strlen(a)次。(注意数组角标从0开始,与for循环在何时截止,不要弄混)。

2、strcmp

对于比较函数,这里说明一下它的原理。大家可能会问字符串又不是数,怎么比较?计算机很聪明的,它会将两个字符串的每一个字符都转换成其对应的ASCII值,然后比较两个字符串的总的ASCII值。对于strcmp(a,b),返回的其实是a的ASCII减去b的ASCII,所以a大返回正值,b大返回负值。一般来说,我们使用strcmp并不是需要比出大小,而是判断返回值是否为0,即a,b字符串是否相同。(严谨的说是组成字符相同且数量相等),比起使用两个for循环一个一个元素比较简洁方便不少。

3、strcpy

笔者一般叫它复制函数,当时记住它出乎意料的容易,因为一个问题:

如何交换两个字符串?

我们都知道如何交换两个整形变量的值,设一个中间变量t,然后t=a;a=b;b=t;但字符串怎么办?字符串可是存在字符数组里的呀,而两个数组是不能用复制号“=”连接的,比如设一个a[20],b[20],是不能a=b,那该怎么解决问题?——strcpy。

#include<stdio.h>
#include<string.h>
int main()
{
	char a[99] = "I Love You";
	char b[99] = "You Love Me";
    char t[99];

	strcpy(t, a);
	strcpy(a, b);
	strcpy(b, t);

	return 0;
}

是不是非常相似?字符串的复制,我们可以联想到整形变量的赋值来理解,知识一下子就好记了。

4、strcat

连接函数,这个笔者做题其实接触不多,其实相应的还有strncat函数,strncat(a,b,n),作用是将b连接到a的末尾,若连接的长度(不包括a的原长)超过n,则截断不要超过n的部分。稍微记一记,当某一天我们遇到问题需要使用它的时候,它肯定能派上大用场。

5、补充

其实有关字符串函数的补充,不止strncat,还有strncpy,strncmp……等等,它们有个特征,就是在str(string字符串)和缩写词的中间有个n,即都多了(int n)这个参数,作用随之有所改变。因为不常使用,这里不再过多赘述,有兴趣的可以自行C一下。

今天我们谈到了字符和字符串的输入问题及一些常用字符串函数的使用技巧,重点是输入问题,常常在我们做题时,因为它导致“思路正确,答案奇怪”,希望大家能好好理解这部分的知识,遇到相关问题能少做不少牢。相关函数也需要我们能灵活运用,避免走弯路,用复杂的方法来达到原本简单目的。

下一篇博客将结合具体题目,讨论有关字符串数组的一些解题思路和技巧,敬请期待!

感谢你能看到这里,希望你有所收获,祝好!

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值