- 背景:想要在c语言中输入包含空格的一行字符串。很明显
scanf("%s",str)
无法实现,因为这个输入方式,遇到空格或者回车符就会停止输入。(备注:全文中出现的str
都表示一个字符串数组,或者char *
)
一. 输入单组数据
- 方法一:使用
gets(str)
来实现
#include<stdio.h>
int main(){
char str[20];
gets(str);
printf("----分界线----\n");
printf("%s\n",str);
return 0;
}
运行结果如下:
注意: 虽然gets()
函数可以实现如上功能,但是该函数被称作一个危险的函数,因为该函数不会判断输入数据的长度是否会超过数组的长度,所以可能会造成数组越界、栈溢出等问题,所以使用的时候必须格外小心。
- 方法二:使用
scanf("%[^\n]",str)
来实现
这里稍作解释一下:%[]
可以用来限制输入的内容,比如%[0-9]
就表示只读入‘0’-‘9’之间的数字,%[a-z]
表示只读入‘a’到‘z’之间的字母。我们这里的%[^\n]
中的^
就表示“非”的意思,\n
就是我们平时所说的回车符,所以%[^\n]
这个符号就是表示只读取 不是\n
的字符。
#include<stdio.h>
int main(){
char str[20];
scanf("%[^\n]",str);
printf("----分界线----\n");
printf("%s\n",str);
return 0;
}
到这里,你以为就结束了?不是不是,还要讲讲这两种输入方式的区别,还有有多组输入数据时的坑。
-----------------------------------------------分界线1-------------------------------------------
二. 输入多组数据时
- 方式一:
测试结果如下:ok没问题#include<stdio.h> int main(){ char str[20]; while(gets(str)!=NULL){ printf("----分界线----\n"); printf("%s\n",str); } return 0; }
- 方式二:
测试结果如下,居然是死循环了。。。。。#include<stdio.h> int main(){ char str[20]; while(~scanf("%[^\n]",str)){ printf("----分界线----\n"); printf("%s\n",str); } return 0; }
为什么呢? 两种方式几乎是做相同的修改,但是运行的结果却是截然不同!
为什么第二种会出现死循环,原因在于%[^\n]
这个符号就是表示只读取 不是\n
的字符,一遇到\n
就停止了。并未对\n
做任何处理,所以当第一次循环完成后,要进行第二次输入时,从缓冲区读取到的第一个字符是上一次遗留下来的\n
,scanf(%[^\n],str)
一遇到\n
又马上停止了,什么都没读入。所以str数组中的内容没有被更新,依旧是上一次读取是留下的内容。那该如何解决呢?- 方法:使用
getchar()
去掉多余的回车符
getchar()
方法的作用就是读取任何一个字符,包括\n
,然后返回。如果没有变量来接收这个返回值,那相当于把读取的这个字符丢弃了。
测试结果如下:#include<stdio.h> int main(){ char str[20]; while(~scanf("%[^\n]",str)){ getchar(); //使用getchar()去掉多余的回车符 printf("----分界线----\n"); printf("%s\n",str); } return 0; }
- 方法:使用
三. 输入多组数据中的坑
输入多组数据时,在非首次输入时,直接按下回车会出现什么情况?
- 方式一:
gets(str)这个方式
- 方式二:
scanf("%[^\n]",str)
配合getchar()
这种方式
- 结果对比分析:
方式一的结果正常,如果直接输入一个回车,那也就输入一个回车,体现出来的就是一行空行。而方式二中,如果直接输入一个回车,输出的内容居然是上一次输入(非回车)的内容。为什么会这样子呢?我们猜测,gets(str)
的方式,如果读入的是一行输入中只有一个回车符,那么它会在str
的第一个位置中放入一个\0
,所以输出的时候就啥都没有了;而scanf("%[^\n]",str)
这个方式,如果读入的是一行输入中只有一个回车符,并不会对str
做任何的改动。 - 想法验证:
- 关于
gets(str)
- 关于
#include<stdio.h>
#include<string.h>
int main(){
char str[20];
int preLen=0,curLen=0,i;
while(gets(str)!=NULL){
preLen=curLen;
curLen=strlen(str);
printf("preLen=%d\n",preLen);
printf("curLen=%d\n",curLen);
for(i=0;i<preLen;i++){
printf("str[%d]=%c\n",i,str[i]);
}
printf("curStr=%s\n",str);
printf("---------------------------\n");
}
return 0;
}
结果如下:
从以上结果可以看出,当第二次输入仅为一个回车符时,gets(str)
会把str
的第一位字符设置为\0
- 关于
scanf("%[^\n]",str)
#include<stdio.h>
#include<string.h>
int main(){
char str[20];
int preLen=0,curLen=0,i;
while(~scanf("%[^\n]",str)){
getchar(); //使用getchar()去掉多余的回车符
preLen=curLen;
curLen=strlen(str);
printf("preLen=%d\n",preLen);
printf("curLen=%d\n",curLen);
for(i=0;i<preLen;i++){
printf("str[%d]=%c\n",i,str[i]);
}
printf("curStr=%s\n",str);
printf("---------------------------\n");
}
return 0;
}
结果如下:
从以上结果可以看出,当第二次输入仅为一个回车符时,curLen
依旧为6,str
依旧保留着上一次输入的内容且未做任何改变。
- 填坑
所以严格一些来说,使用scanf("%[^\n]",str)
配合getchar()
这种方式进行多组输入时,必需确保在每一次输入前,字符串数组已被重新初始化。
所以使用scanf("%[^\n]",str)
配合getchar()
这种方式进行多组输入时的改进版本如下:
#include<stdio.h>
#include<string.h>
int main(){
char str[20];
while(~scanf("%[^\n]",str)){
getchar();
printf("----分界线----\n");
printf("%s\n",str);
memset(str,0,sizeof(str)); //在每一组数据的最后,把数组重新初始化 !!!!
}
return 0;
}