C Primer Plus 第11章 字符串和字符串函数 11.2 字符串输入

原创
2016/08/19 05:36
阅读数 50

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( )。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部