使用scanf函数接收字符串时,如果不加以说明,scanf函数是不会接收空格的,一旦读取到空格scanf就停止接受。如果一定要读取到空格,就需要用到扫描字符集%[],[]内可以加入不想要读取到的字符,直到读取到目标字符,scanf才会停下。
目录
scanf函数
扫描字符集
gets函数
fgets函数
scanf函数
如果要用scanf函数读取空格首先要了解扫描字符集的概念。
扫描字符集
明解C语言中这样描述扫描字符集:扫描字符集(scanset)元素的非空序列。实参必须为指向字符型数组第一个字符的指针。该数组的长度须足够容纳所有字符序列以及null字符。该转换会自动添加一个表示字符串末尾的null字符。
转换说明符为左方括号[与右方括号]之间的格式控制字符串中的所有字符序列。当紧跟在左方括号后面的字符不是折音符^时,扫描字符集由左右方括号之间的扫描列表(scanlist) 构成。当紧跟在左方括号后面的字符是^时,扫描集为未出现在^与右方括号之间的扫描列表中的所有字符。当转换说明符以[]或字符开始时,第一个右方括号为扫描列表中的一一个字符元素,而第二个出现的右方括号才是转换说明的结束符。当转换说明符不以[]和[^]开始时, 第一个出现的右方括号就是转换规范的结束符。当扫描列表中含有连字符-,并且既非第一个字符(如果以^开头,则为第二个字符)也非最后一个字符时,其定义因编译器而异。
乍一看这么长一定很难,但如果用代码的形式来表示就简单许多:
1、[]之间没有^时:
#includeint main() { char str[10] = ""; //1、无^时:只读取[]内存在的字符,一旦输入的字符不在[]中,停止读取 scanf("%[abcde]", &str); printf("%sn", str); return 0; }
样例如:
2、[ 后紧跟着^时:
#includeint main() { char str[10] = ""; scanf("%[^b]", &str); //2、有^时:读取直到n停止——只要^和]之间的所有字符未出现时,scanf的读取就不会停止 printf("%sn", str); return 0; }
样例:
对于画删除线的部分,其实是有些不妥的,如果是以]开头,其意义同其他字符一样,并不会什么都不读取,如:
而]不在开头的情况下,结果在VS2019环境下并不符合:
至于扫描集中含有连字符的情况结果视编译器而定,大家可以自行尝试。
那么用scanf函数输入空格的方法为:
scanf("%[^n]", &str);
gets函数
顾名思义,gets即get string的缩写,作用即为获取字符串,printf和scanf分别为带格式化字符串的输入输出,而gets和puts则为不带格式化的字符串的输入输出。使用gets可以直接读取到空格以及任何非换行符"n"的字符,输入"n"则中断输入。看起来gets函数非常的方便,但当我们在VS2015以及以后版本的环境下使用gets函数时会报警告:
而gets的头文件为
虽然在VS上我们依旧可以使用该函数,不过事实上VS2015起该函数已经不存在了。移除该函数的原因也很简单,该函数不安全。gets没有指定输入的字符串的大小,那么就会造成一个隐患:当遭到恶意大量输入时,很容易发生溢出。 从安全的角度来看,gets函数最好不再使用。
作为替代,像scanf_s一样,也有一个函数gets_s作为替代,不过二者的用法不太一样,gets_s除了输入字符串的内容以外,还需要另指定一个参数来确定字符串的长度,(超出的部分会被截断)。
char *gets( char *str ); char *gets_s( char *str, rsize_t n );
当然使用gets_s也会有更多的限制:
fgets函数
虽然gets_s可以解决gets存在的安全问题,不过并不是在所有的编译器上都可用,它并不是通用函数,而fgets既能解决gets函数的安全问题,也能保证在每个编译器上可用。
char *fgets( char *str, int count, FILE *stream );//(C99 前) char *fgets( char *restrict str, int count, FILE *restrict stream );//(C99 起)
第一个参数为数据存放的数组地址,第二个参数为最大长度,第三个参数为输入源,从键盘上输入则输入源为stdin.如果count<=1并不会报错,但不会读取数据。与gets_s一样,如果输入字符串长度大于最大长度,那么超出部分依然会被截断。实例:
当然输入的字符串内容也要保证长度小于数组长度:
不管是gets_s还是fgets都可以用来替代gets函数,因此尽可能地避免使用gets函数吧。