gets,fgets,scanf与字符串的故事

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时,代表扫描到换行结束输入,这样做就可以读入空格了。

猜你喜欢

转载自blog.csdn.net/weixin_42201172/article/details/81259229
今日推荐