1.gets
gets是从标准输入设备读取字符串的函数,函数原型 char * gets ( char * str ); 刚开始学习C语言时觉得gets这个函数相当的好用,因为他比scanf写起来要短,而且可以输入有空格的字符串。但是越往后学就会发现,在C语言中用GCC编译使用了gets函数的源文件,编译器会给警告。
warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
警告: the `gets' function is dangerous and should not be used.
原因是gets可以无限读取,不会判断上限,如果无限输入会造成栈空间溢出,在程序返回时,不能正常的找到返回地址,程序将发生不可预测行为。如果用了gets,像我们现在写的小程序可能在PC端上会闪退,但如果在大型项目中就会造成很严重的损失。因此我们应该尽量避免使用gets函数,顺便一提在2011年12月,ANSI 采纳了 ISO/IEC 9899:2011 标准,标准中删除了 gets()函数,使用一个新的更安全的函数gets_s()替代。
如果不用gets想要输入字符串,就要用到fgets和scanf了
2.fgets
函数原型char *fgets(char *buf, int bufsize, FILE *stream);*buf: 字符型指针,指向用来存储所得数据的地址。bufsize: 整型数据,指明存储数据的大小。*stream: 文件结构体指针,将要读取的文件流。
fgets函数的调用形式如下:fgets(str,n,fp);此处,fp是一个文件指针;str是存放在字符串的起始地址;n是一个int类型变量。函数的功能是从fp所指文件中读入n-1个字符放入str为起始地址的空间内;如果在未读满n-1个字符之时,已读到一个换行符或一个EOF(文件结束标志),则结束本次读操作,读入的字符串中最后包含读到的换行符。因此,确切地说,调用fgets函数时,最多只能读入n-1个字符。读入结束后,系统将自动在最后加'\0',如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
下面是一个例子:
#include <stdio.h>
int main()
{
FILE *fp;
char str[60];
fp = fopen("file.txt" , "r");
if(fp == NULL) {
perror("打开文件时发生错误");
return(-1);
}
if( fgets (str, 60, fp)!=NULL ) {
puts(str);
}
fclose(fp);
return(0);
}
假设我们有一个文件file.txt 。他的内容如下:
Xiyou Linux Group!
编译上面的代码可以得到结果:
Xiyou Linux Group!
3.scanf
函数原型int scanf(char *format[,argument,...]);scanf()函数是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。其调用格式为: scanf("<格式化字符串>",<地址表>);scanf()函数返回成功赋值的数据项数,出错时则返回EOF。空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符,空白符可以是space,tab,newline等等,直到第一个非空白符出现为止。一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。
那么怎么才能用scanf输入字符串呢?可以采用如下的两种格式符。
%c 逐个输入字符 %s 整体一次输入字符串
举个例子
#include <stdio.h>
int main()
{
char a[18];
int i;
for(i=0;i<17;i++){
scanf("%c",&a[i]);
}
for(i=0;i<17;i++){
printf("%c",a[i]);
}
printf("\n");
}
上面这段代码是用%c逐个输入字符的情况,%c有很大的局限性,因为输入字符数必须和循环的次数相等,不会自动加结束符号\0。我们用几组数据测试一下:
input:xiyou linux group
output:xiyou linux group
input:abcdefghigklmnopqrstuvwxyz
output:abcdefghigklmnopq
input:abc
output:abc
第一组数据正好用了17个字符,成功输出;第二组数据超出了数组大小,因为循环时已经设定了预计输入字符数,所以超出的部分并没有输进去;第三组数据虽然只显示了三个字符,但其实输入时需要在后面补足空格才能正常结束输入。
而%s
#include <stdio.h>
int main()
{
char a[20];
scanf("%s",a);
printf("%s\n",a);
}
我们输入几组测试数据:
input:xiyoulinuxgroup
output:xiyoulinuxgroup
input:xiyou linux group
output:xiyou
input:qwertyuiopasdfghjklz
output:qwertyuiopasdfghjklz
*** stack smashing detected ***: <unknown> terminated
已放弃 (核心已转储)
由这几组数据可以看出,当用%s输入时可以输入任意不超过数组大小的字符,但因为scanf()中空格是多个字符串的分隔符,所以企图用此法输入带空格的字符串给一个字符数组时,只有第一个空格前的字符串有效。
那么怎样才能用scanf()输入带空格的字符串呢?(不用%c)
现在感觉scanf扫描集这玩意儿真好用啊~~~
下面介绍scanf()的扫描集,我们既可以用fgets来替换gets去实现输入字串包含空格,也可以用scanf的扫描集来实现这一功能。
#include <stdio.h>
int main( int argc,char *argv[] )
{
char str[20];
printf("input:");
scanf("%[^\n]",str);
printf("output:%s\n",str);
return 0;
}
inout:xiyou linux group
output:xiyou linux group
成功的输入了一个带空格的字符串!
上面的代码其实是scanf扫描集的一种用法,那么什么是scanf扫描集呢?
扫描集定义一个字符集合,可由 scanf() 读入其中允许的字符并赋给对应字符数组。 扫描集合由一对方括号中的一串字符定义,左方括号前必须缀以百分号。
具体作用是:如果输入的字符属于方括号内字符串中某个字符,那么就提取该字符;如果一经发现不属于就结束提取。该方法会自动加上一个'\0'到已经提取的字符后面。
下面来看一个例子:
#include <stdio.h>
int main( int argc,char *argv[] )
{
char str[10];
printf("input:");
scanf("%[abc]",str);
printf("output:%s\n",str);
return 0;
}
运行结果:
input:abcdefg
output:abc
从结果我们可以看出,scanf只接受了abc这三个字符,并没有接受除方括号内字母之外的字符。而且输出时只输出了abc,并没有输出其它乱码,可见其已在abc后自动加入了'\0'。
在“[ ]”内,还可以加入另外一个字符来修饰它的作用:“^”。这个符号可以理解为“补集”,即,扫描除方括号之内的其它字符:如果输入的字符不属于方括号内字符串中某个字符,那么就提取该字符;如果一经发现输入的字符属于该字符,则结束。接下来对上面的例子做一个小修改,再来看一下这种“补集”用法:
#include <stdio.h>
int main( int argc,char *argv[] )
{
char str[10];
printf("input:");
scanf("%[^abc]",str);
printf("output:%s\n",str);
return 0;
}
运行结果:
input:1234cba
output:1234
由这个例子就可以明白了当补集里为\n时,代表扫描到换行结束输入,这样做就可以读入空格了。