“scanf扫描集”的初体验

来源:互联网 发布:淘宝网是马云的吗 编辑:程序博客网 时间:2024/06/11 15:13

        前天在讲座中,见到了一种奇葩的scanf的表达格式,听说叫做scnaf的“扫描集”。在听学长讲解了它的功能后,觉得scanf的这一功能甚是有用,于是在网上搜集了一些有关scanf扫描集的信息,自己一试,果然很有用。

        ANSI C 标准向 scanf() 增加了一种新特性,称为扫描集(scanset)。 扫描集定义一个字符集合,可由 scanf() 读入其中允许的字符并赋给对应字符数组。 扫描集合由一对方括号中的一串字符定义,左方括号前必须缀以百分号。

        具体作用是:如果输入的字符属于方括号内字符串中某个字符,那么就提取该字符;如果一经发现不属于就结束提取。该方法会自动加上一个'\0'到已经提取的字符后面。

        下面来看一个简单的关于scanf扫描集的例子:

#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:abcdefgoutput: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:1234cbaoutput:1234

        从这个结果可以看出,除了"abc"之外的字符已经顺利地被scanf所接受,而一遇到"[ ]"内的字符——abc,则立即结束扫描,将之前扫描到的字符存储到数组str中。


        利用scanf的这一功能特点,我们可以去写出安全性更高的代码来。如果我们有输入的字串包括空格的需要时, 我们常常会用到gets函数。在GCC中,如果我们用gets函数去进行包含空格的输入操作,编译程序时,编译器会直接报出一个警告:

警告: 不建议使用‘gets’(声明于 /usr/include/stdio.h:638) [-Wdeprecated-declarations]
        原因就在于,gets不会去检查字符串的长度,如果字符串过长就会导致溢出。如果溢出的字符覆盖了其他一些重要数据就会导致不可预测的后果。在man手册里也有关于gets这样的警告:

        Never use gets().  Because it is impossible to tell without knowing the data in advance how many  characters  gets()  will  read,  and  because gets() will continue to store characters past the end of the buffer, it is extremely dangerous to use.  It has  been  used  to  break  computer security.

        我们既可以用fgets来替换gets去实现输入字串包含空格,也可以用scanf的扫描集来实现这一功能,只要在方括号中写入“^\n”,即:直到输入了回车才停止扫描。下面来演示这一用法:

#include <stdio.h>int main( int argc,char *argv[] ){    char str[20];    printf("input:");    scanf("%[^\n]",str);    printf("output:%s\n",str);                                                      return 0;}
        运行结果:

input:how are you?output:how are you?

        但是,如果我们想在遇到大写字母的时候就停止输入,难道我们要在方括号中写上ABCDEFGHIJK……UVWXYZ吗?

当然不,如果真的要这样做,那这就不是我们伟大的C语言了。我们可以这样来进行缩写:[^A-Z]

#include <stdio.h>int main( int argc,char *argv[] ){    char str[20];    printf("input:");    scanf("%[^A-Z]",str);    printf("output:%s\n",str);    return 0;}
        运行结果:

input:abcdefgGODoutput:abcdefg

        如果我们想要以一个字符串做为结束标志呢?比如,以“?!”这两个符号的整体作为结束的标志:

#include <stdio.h>int main( int argc,char *argv[] ){    char str[20];        printf("input:");       scanf("%[^?!]",str);    printf("output:%s\n",str);    return 0;}
        运行结果:

input:good?fantastic?!output:good
        看来是不行,在遇到了第一个“?”的时候就结束了输入。那我们加上括号呢?

int main( int argc,char *argv[] ){    char str[20];        printf("input:");       scanf("%[^(?!)]",str);    printf("output:%s\n",str);    return 0;}
        运行结果:

input:good(good?)good?!output:good

        看来,尝试去用一个字符串的整体去作为结束的标志是不可行的,扫描集还是严格去比对每一个字符的。


        还从来没有有过这个扫描集,今天学习了一下,觉得还是很好用的,在某些场合下用扫描集会方便许多 。以后多多去尝试吧,相信这个东西会派上大用场。