标准I/O----get与put函数族小结:

由于Input(get)与Output(put)函数族中包含的函数比较多,我们将其分为:“一次处理一个字符的输入输出”和“一次处理一行的输入输出”两部分进行总结。 以上两种输入输出都属于非格式化I/O,第三种非格式化I/O为fread和fwrite(也称作直接I/O)。而格式化I/O则是printf与scanf函数家族。

一、一次处理一个字符的I/O:

1、函数原型与解析:

(1)、原型与参数返回值:
这一类函数都是处理单个字符char(c)所以get、put的后缀要么是c要么是char。

/*Input:man getc*/
int getc(FILE * stream);/*从流式文件stream中读取一个字符*/
int fgetc(FILE * stream);/*从流式文件stream中读取一个字符*/
int getchar(void);/*is equivalent to getc(stdin),从标准输入中读取一个字符*/

/*返回值:
成功:返回获取到的字符,虽然返回值为int型,但是char类型本就是以整数形式存储,所以无伤大雅;
出错或者到达文件尾:返回EOF(End Of File),EOF(整数-1)为文本文件结束标志(因为文本文件的有效内容不存在ASCII为-1的字符)。
*/

/*Output:man putc*/
int putc(int c,FILE * stream);
/*c为要输入的字符的ASCII值,cast an unsigned char to stream,当然可直接以带单引号的单个字符形式作为参数*/
int fputc(int c,FILE * stream);
int putchar(void);/*is equivalent to putc(c,stdout)*/

/*返回值:
成功:返回c
出错:返回EOF
*/

(2)、要点解析:
getc()与fgetc()的用法基本相同,区别在于getc()可以以宏定义的方式实现,而fgetc()只能以函数的方式实现。这就意味着调用getc()所需的时间比fgetc()要短许多。但是注意宏定义的优缺点是选择这两者的主要标准(采用宏定义的方式代替定义函数的优缺点):

优点:

①提高了程序的可读性,同时也方便进行修改;
提高程序的运行效率:使用带参的宏定义既可完成函数调用的功能,又能避免函数的出栈与入栈操作,减少系统开销,提高运行效率;
③宏是由预处理器处理的,通过字符串操作可以完成很多编译器无法实现的功能。比如##连接符。

缺点:

① 由于是直接嵌入的(所有调用全部替换),所以代码可能相对多一点;
② 嵌套定义过多可能会影响程序的可读性,而且很容易出错;
③对带参的宏而言,由于是直接替换,并不会检查参数是否合法,存在安全隐患

而宏的优缺点并非代表了getc()与fgetc()的优缺点,起码getc()作为函数,其函数名就是一个地址,故而可用做参数。putc()与fputc()的区别也是一样的道理。

2、小小测试:

(1)、测试putc()与getc():

这里写图片描述

在putc()写入一个字符“1”之后成功返回写入的字符。并且写入完毕后,重新打开文件读到的第一个字符即为“1”。getc()读取成功并成功返回。但是当我们在写入完毕之后不关闭重新打开,由于写了一次,文件指针自动后移一次,直接getc()获取到的会是文件结束标志EOF所以返回EOF打印输出为‘1’。(笔者对于这个问题有点不太确定,但依据个人理解作出判断,有疑问的请看EOF是何时加入的),测试结果如下:

这里写图片描述

(2)、测试getchar()与putchar()(演化为fgetc(stdin)、fputc(c,stdout)):
先调用函数fputc(‘1’,stdout);在标准输出中输出字符’1’,然后在标准输入中等待输入,在输入字符’a’以后,将stdin文件中的内容返回值再输出。得到的返回值便是输入的当前字符。
这里写图片描述

二、一次处理一行的I/O:

1、函数原型与解析:

(1)、原型与参数返回值:
这一类函数都是处理一行字符即字符串string(s)所以get、put的后缀都是s。

/*Input(每次读取一行到缓冲区中):man gets*/
char * fgets(char * buf, itn size, FILE * stream);/*从指定的流式文件stream中向定义的缓冲区buf中写入数据*/
char * gets(char * buf);/*从标准输入流式文件stdin(键盘)向buf中写入数据*/

/*返回值:
成功:返回buf的首地址;
出错或者已到达文件尾:返回NULL
*/
/*Output(每次从缓冲区中输出一行):man puts*/
int fputs(const char * buf, FILE * stream);/*将buf中的数据输出到指定的流式文件stream中*/
int puts(const char * buf);/*将buf中的数据输出到标准输出中(显示屏)*/
/*返回值:
成功:返回非负值
出错失败:返回EOF
*/

(2)要点解析与图解分析:

图解
这里写图片描述
要点

输入:对于fgets函数必须制定读取长度size,当读取的一行长度小于缓冲区size-1且遇到换行时自动截止,读取的字符串被送入缓冲区,缓冲区以NULL字节结尾;当读取一行长度大于size-1时,只读取fgets只会返回读到的0~szie-1的字符,缓冲区以NULL结尾,当循环读取时,下一次接着读取该行直到读取到换行或长度大于size-1。对于gets不能指定缓冲区大小,当读取时溢出会继续写到缓冲区之后的空间而产生安全隐患,所以尽量不要使用。

输出:fputs()将以NULL字节终止的字符串写到指定的流文件中,写到stdout中我们称之为输出。终止符NULL不写出,注意:这并不一定是每次将buf中一行写入流文件中,因为字符串不需要换行符作为最后一个非NULL字符,通常NULL之前是一个换行符,但并非要求必须如此。puts()与基本fputs()相同,只是puts()的流文件是设定好的stdout,且puts()在读到终止符(NULL)时不输出终止符但会写一个换行符到标准输出中。虽然puts()并没有gets()所带来的安全隐患,但是还是尽量避免使用,那就要求我们在使用fputs()与fgets()时自己处理回车换行问题。(似乎有点绕,请看测试程序)

2、小小测试:

这里写图片描述

上图中fputs()对于换行符进行处理一串字符串输出为两行,字符串末尾没有回车故shell接着在stdout中输出,puts()则对于末尾没有换行的字符串会自动加上换行。而对于单独的’\n’与’\r’以及’\0’fputs()处理结果如下图所示:

这里写图片描述
对于普通流式文件的写入与读取操作与在stdin与stdout中的法则是一样的。

三、标准I/O的效率问题:

对于函数的选择方面,我们一方面是看实用性或者功能性,比如要处理字符串就选用‘s’后缀,而以单个字符作为处理单位就需要选用’c’后缀(getchar()被视为getc(stdin)、putchar()被视为putc(stdout)等);另一方面程序的效率问题也是很重要的。接下来我们对于宏定义(getc()与putc())与函数(fgetc()与fputc())的效率作以测试:

1、测试源代码:

/*
时间:2017年1月20日01:05:10
测试环境:Redaht 6.4
*/
/*write.c先写一个比较大的文件便于测试*/
#include<stdio.h>
#define MAX 10000 /**修改2次得到2个不同大小的文件以测试2次**/
int main (void)
{
    FILE * fp = fopen("getc.txt","w+");
    int i = 0;
    char buf[] = "abcdefghijklmnopqrstuvwxyz";
    for(i; i < MAX; i++)
        fwrite(buf,1,sizeof(buf),fp);
    fclose(fp);
    return 0;
}
/*getc.c测试宏定义的*/
#include<stdio.h>

int main (void)
{
    FILE * fp = fopen("getc.txt","w+");
    int ret = 0;
    while( (ret = getc(fp)) != EOF)
        putc(ret,stdout);
    fclose(fp);
    return 0;
}
/*fgetc.c测试函数*/
#include<stdio.h>
int main (void)
{
    FILE * fp = fopen("getc.txt","w+");
    int ret = 0;
    while( (ret = fgetc(fp)) != EOF)
        fputc(ret,stdout);
    fclose(fp);
    return 0;
}

2、测试结果:

文件较小时:
这里写图片描述

文件较大时:
这里写图片描述

参考书籍:Advanced Programming in the Unix Environment(Third Edition)(APUE),(W.Richard Stevens、W.Richard Stevens著)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值