C语言笔记 -- I/O

标准库IO

fopen

fopen函数原型如下:

#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
//返回值:成功返回文件指针,出错返回NULL并设置errno

其中,mode参数是一个字符串,+表示增强功能,"r+"表示不仅可以读,还可以写;"w+"表示不仅可以写,还可以读;"a+"表示不仅可以追加数据,还可以读;
FILE是C标准库中定义的结构体类型,其中包含该文件在内核中标识(即文件描述符)、 I/O缓冲区和当前读写位置等信息,但调用者不必知道FILE结构体都有哪些成员,调用者只是把文件指针在库函数接口之间传来传去,而文件指针所指的FILE结构体的成员在库函数内部维护,调用者不应该直接访问这些成员,这种编程思想在面向对象方法论中称为封装(Encapsulation)。像FILE *指针称为文件句柄(Handle) , FILE *指针就像一个把手(Handle),抓住这个把手就可以打开门或抽屉,但用户只能抓这个把手,而不能直接抓门或抽屉。

errno与perror函数

很多系统函数在错误返回时将错误原因记录在libc定义的全局变量errno中,每种错误原因对应一个错误码,可以查阅errno的Man Page了解各种错误码, errno在头文件errno.h中声明,是一个整型变量,所有错误码都是正整数。
如果在程序中打印错误信息时直接打印errno变量,打印出来的只是一个整数值,仍然看不出是什么错误。比较好的办法是用perror或strerror函数将errno解释成字符串再打印。

#include <stdio.h>
void perror(const char *s);

perror函数将错误信息打印到标准错误输出,首先打印参数s所指的字符串,然后打印:号,然后根据当前读取的errno的值打印错误原因。
errno是一个全局变量,很多系统函数都会改变它。所以一个系统函数错误返回后应该马上检查errno,在检查errno之前不能再调用其它系统函数。

以字节为单位的标准库IO函数

fgetc函数从指定的文件中读一个字节, getchar函数从标准输入读一个字节,调用getchar()相当于调用fgetc(stdin)。

#include <stdio.h>
int fgetc(FILE *stream);
int getchar(void);
//返回值:成功返回读到的字节,出错或者读到文件末尾时返回EOF

函数原型中FILE *指针参数有时会起名叫stream,这是因为标准I/O库操作的文件有时也叫做流(Stream),文件由一串字节组成,每次可以读或写其中任意数量的字节。这与TCP协议中流的概念一致。
对于fgetc函数的使用有以下几点说明:

  • 要用fgetc函数读一个文件,该文件的打开方式必须是可读的。
  • 系统对于每个打开的文件都记录着当前读写位置在文件中的地址(或者说距离文件开头的字节数),也叫偏移量(Offset)。当文件打开时,读写位置是0,每调用一次fgetc,读写位置向后移动一个字节,因此可以连续多次调用fgetc函数依次读取多个字节。
  • fgetc成功时返回读到一个字节,本来应该是unsigned char型的,但由于函数原型中返回值是int型,所以这个字节要转换成int型再返回,那为什么要规定返回值是int型呢?因为出错或读到文件末尾时fgetc将返回EOF,即-1,保存在int型的返回值中是0xffffffff。如果读到字节0xff,由unsigned char型转换为int型是0x000000ff,只有规定返回值是int型才能把这两种情况区分开。如果规定返回值是unsigned char型,那么当返回值是0xff时无法区分到底是EOF还是字节0xff。如果需要保存fgetc的返回值,一定要保存在int型变量中,如果写成unsigned char c = fgetc(fp);,那么根据c的值又无法区分EOF和0xff字节了。注意, fgetc读到文件末尾时返回EOF,只是用这个返回值表示已读到文件末尾,并不是说每个文件末尾都有一个字节是EOF(根据上面的分析, EOF并不是一个字节)。

fputc函数向指定的文件写一个字节, putchar向标准输出写一个字节,调用putchar(ch)相当于调用fputc(ch, stdout)。

#include <stdio.h>
int fputc(int c, FILE *stream);
int putchar(int c);
//返回值:成功返回写入的字节,出错返回EOF

对于fputc函数的使用也要说明几点:

  • 要用fputc函数写一个文件,该文件的打开方式必须是可写的(包括追加)。
  • 每调用一次fputc,读写位置向后移动一个字节,因此可以连续多次调用fputc函数依次写入多个字节。但如果文件是以追加方式打开的,每次调用fputc时总是将读写位置移到文件末尾,然后把要写入的字节追加到后面

从终端设备(标准输入)读数据有两个特点:
1.如果用户没有输入字符, getchar函数就阻塞等待,所谓阻塞是指这个函数调用不返回,也就不能执行后面的代码,这个进程阻塞了,操作系统可以调度别的进程执行。
2.用户输入一般字符并不会使getchar函数返回,仍然阻塞着,只有当用户输入回车或者到达文件末尾(EOF)时getchar才返回。
从终端设备输入时有两种方法表示文件结束,一种方法是在一行的开头输入Ctrl-D(如果不在一行的开头则需要连续输入两次Ctrl-D)(Ubuntu下Ctrl-D无效),另一种方法是利用Shell的Heredoc语法:在执行可执行文件的命令行中添加<<END,<<END表示从下一行开始是标准输入,直到某一行开头出现END时结束。 <<后面的结束符可以任意指定,不一定得是END,只要和输入的内容能区分开就行。

操作读写位置的函数

fseek可以任意移动读写位置, ftell可以返回当前的读写位置。rewind函数把读写位置移到文件开头。

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
// 返回值:成功返回0,出错返回-1并设置errno
long ftell(FILE *stream);
// 返回值:成功返回当前读写位置,出错返回-1并设置errno
void rewind(FILE *stream);

fseek的whence和offset参数共同决定了读写位置移动到何处, whence参数的含义如下:
SEEK_SET
从文件开头移动offset个字节
SEEK_CUR
从当前位置移动offset个字节
SEEK_END
从文件末尾移动offset个字节
offset可正可负,负值表示向前(向文件开头的方向)移动,正值表示向后(向文件末尾的方向)移动,如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸,从原来的文件末尾到fseek移动之后的读写位置之间的字节都是0。

以字符串为单位的标准库I/O函数

#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
char *gets(char *s);
// 返回值:成功时s指向哪返回的指针就指向哪,出错或者读到文件末尾时返回NULL

fgets函数,参数s是缓冲区的首地址, size是缓冲区的长度,该函数从stream所指的文件中读取以’\n’结尾的一行(包括’\n’在内)存到缓冲区s中,并且在该行末尾添加一个’\0’组成完整的字符串
如果文件中的一行太长, fgets从文件中读了size-1个字符还没有读到’\n’,就把已经读到的size-1个字符和一个’\0’字符存入缓冲区,文件中剩下的半行可以在下次调用fgets时继续读。
如果一次fgets调用在读入若干个字符后到达文件末尾,则将已读到的字符串加上’\0’存入缓冲区并返回,如果再次调用fgets则返回NULL,可以据此判断是否读到文件末尾。
注意,对于fgets来说, ‘\n’是一个特别的字符(因为fgets读取以’\n’结尾的一行到缓冲区中),而’\0’并无任何特别之处,如果读到’\0’就当作普通字符读入。如果文件中存在’\0’字符(或者说0x00字节),调用fgets之后就无法判断缓冲区中的’\0’究竟是从文件读上来的字符还是由fgets自动添加的结束符,所以fgets只适合读文本文件而不适合读二进制文件,并且文本文件中的所有字符都应该是可见字符,不能有’\0’
fputs向指定的文件写入一个字符串, puts向标准输出写入一个字符串。

#include <stdio.h>
int fputs(const char *s, FILE *stream);
int puts(const char *s);
// 返回值:成功返回一个非负整数,出错返回EOF

缓冲区s中保存的是以’\0’结尾的字符串, fputs将该字符串写入文件stream,但并不写入结尾的’\0’(因为文本文件中不应该包含字符’\0’)。与fgets不同的是, fputs并不关心的字符串中的’\n’字符,字符串中可以有’\n’也可以没
有’\n’。 puts将字符串s写到标准输出(不包括结尾的’\0’),然后自动写一个’\n’到标准输出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值