11.2.1 创建存储空间
要做的第一件事是建立一个空间以存放读入的字符串。
最简单的办法就是在声明中明确指出数组的大小:
char name[81] ;
现在的name是一个已经分配81字节存储块的地址。另一个方法是使用C库里分配存储空间的函数,这一点会在第12章讨论。
为字符串预留空间后,就可以读取字符串了。C库提供了三个读取字符串的函数:scanf( )、gets( )和fgets( )。我们先讨论最常用的gets( )。
11.2.2 gets( ) 函数
它从系统的标准输入设备(通常是键盘)获取一个字符串。因为字符串没有预定的长度,所以gets( )需要知道何时结束。解决办法是读字符串直到遇到一个换行字符(\n),按回车键可以产生这个字符。它读取换行符(不包括换行符)之前的所有字符,在这些字符后添加一个空字符(\o),然后把这个字符串交给调用它的程序。它将读取换行符并将其丢弃,这样下一次读取就会在新一行开始。
程序清单11.4 name1.c程序
/*name1.c 读取一个名字*/
#include<stdio.h>
#define MAX 81
int main(void)
{
char name[MAX]; /*分配空间*/
printf("Hi,what's your name?\n");
gets(name); /*把字符串放进name数组*/
printf("Nice name ,%s.\n",name);
return 0;
}
程序清单11.4接受并存储最多80个字符(包括空格)的任何名字(记住为数组里的\o预留空间)。注意到希望gets( )改变调用函数中的某个变量(name),也就是说应当使用一个地址作为参数;当然,数组名正是一个地址。
get( )函数的使用可以比前的例子更为复杂,请参见程序清单11.5.
程序清单11.5 name2.c程序
/*name1.c 读取一个名字*/
#include<stdio.h>
#define MAX 81
int main(void)
{
char name[MAX]; /*分配空间*/
char * ptr;
printf("Hi,what's your name?\n");
ptr=gets(name); /*把字符串放进name数组*/
printf("Nice name, %s.\n",ptr);
return 0;
}
get( )函数通过两种方式获取输入:
**它使用一个地址,把字符串赋给name;
**gets( )的代码使用return关键字返回字符串的地址,程序把这个地址分配给ptr。注意到ptr是一个char的指针,这意味着gets必须返回一个指向char的指针值。
ANSI C要求stdio.h头文件包括gets( )的函数原型。您不需要亲自声明这个函数,只须记住包含这个头文件即可。gets( )函数的构造如下:
char *gets(char *s)
{
...
return (s);
}
这个函数头说明gets( )返回一个指向char的指针。请注意,gets( )返回的指针与传递给它的是同一个指针。输入字符串只有一个备份,它放在作为函数参数传递过来的地址,因此程序清单11.5中的ptr最后也指向name。gets( )函数实际的构造更复杂一点,因为它有两个可能的返回值:如果一切都顺利,它返回的是读入字符串的地址,正如上面所说的。如果出错或gets( )遇到文件结尾,它就返回一个空(0)地址。这个空地址被称为空指针,并用stdio.h中定义的常量NULL来表示。因此,gets( )中还加入了一些错误检测,这使它可以很方便的使用如下形式使用:
while (gets(name)!=NULL)
这个的指令使您即可以检查是否到了文件的结尾,又可以读取一个值。如果遇到了文件的结尾,name中什么也不会读入。这种一举两得的方法就比getchar( )函数所采用的方法简洁的多,getchar( )只返回一个值而没有参数。
while ((ch=getchar())!=EOF)
附带提一下,不要混淆空指针和空字符。空指针是一个地址,而空字符是一个char类型的数据对象,其值为0.数值上两者都可以用0表示,但是它们的概念不同:NULL是一个指针,而0是一个char类型的常量。
11.2.3 fgets( )函数
gets( )的一个不足是它不检查预留存储区是否能够容纳实际输入的数据。多出来的字符简单地溢出到相邻的内存区。fgets( )函数改进了这个问题,它让您指定最大读入字符数。由于fgets( )是为文件I/O设计的,在处理键盘输入时就不如gets()那么方便。fgets()和gets()有三方面不同:
1、它需要第二个参数来说明最大读入字符数。如果这个参数值为n,那么fgets( )就会读取最多n-1个字符或者读完一个换行符为止,由这二者中最先满足的那个来结束输入。
2、如果fgets( )读取到换行符,就会把它存放在字符串里,而不是像gets()那样丢弃;
3、它还需要第三个参数来说明读哪个文件。从键盘上读取输入时,可以使用stdin作为该参数,这个标识符在stdio.h中定义。
程序清单11.6使用了fgets( )代替了11.5里的gets( )。
程序清单11.6 name3.c 程序
/*name1.c 读取一个名字*/
#include<stdio.h>
#define MAX 81
int main(void)
{
char name[MAX]; /*分配空间*/
char * ptr;
printf("Hi,what's your name?\n");
ptr=fgets(name,MAX,stdin); /*把字符串放进name数组*/
printf("Nice name, %s.\n",ptr);
return 0;
}
问题在于fgets( )把换行符存储到字符串里,这样每次显示字符串时就会显示换行符。本章后面“其他字符串函数”小节的结尾会介绍如何用strchr( )来定位和删除换行符。
对于重要的编程,应当使用fgets( )而不是gets( )。
11.2.4 scanf( )函数
scanf( ) 和get( )的主要差别在于它们如何决定字符串何时结束。
scanf()更基于如何获取单词(get word)而不是获取字符串(get string);而gets( )函数,正如您所看到的,会读取所有字符,直到遇到第一个换行符为止。
scanf( )使用两种方法来决定输入结束。无论哪种方法,字符串都是以遇到第一个非空白字符开始。如果使用%s格式,字符串读到下一个空白字符(但不包括)(比如空格、制表符和换行符)。如果指定了字段的宽度,比如%10s,scanf( )就会读入10个字符或直到遇到第一个空白字符,由二者中最先满足的那一个终止输入。
程序清单11.7举例说明了指定字段宽度时scanf( )的工作情况。
/*scan_str.c 使用scanf()*/
#include<stdio.h>
int main(void)
{
char name1[11],name2[11]; /*分配空间*/
int count;
printf("Please inter 2 names.\n");
count=scanf("%5s %10s",name1,name2);
printf("I read the %d names %s and %s.\n",
count,name1,name2);
return 0;
}
第三个例子输出:
Please enter 2 names .
portensia callowit
I read 2 names Porte and nsia.
portensia的后4个字母被读到name2中,这是因为第二次调用scanf( )时,它在第一个调用 结束的地方继续开始读取输入数据。
根据所需输入的特点,用gets()从键盘读取文本可能要更好。scanf()主要用于以某种标准形式输入的混合类型数据的读取和转换。例如,每一个输入行都包括一种工具名称、库存数量和单价,您就可以使用scanf();否则您必须在函数中自己处理输入错误的检测。如果希望一次只输入一个单词,最好使用scanf( )。
来源:oschina
链接:https://my.oschina.net/u/2754880/blog/734882