缓冲输入输出

用户 - 缓冲I/O

块大小

实际应用中, 快大小一般是512字节, 1024字节, 2048字节,或4096字节.
效率的大规模提升只是通过每次操作的数据设置为快大小的整数倍或者约数倍获得的. 这是因为内核和硬件之间是通过块交互的. 所以使用块大小保证请求是块对齐的, 可以防止无关的内核操作.

标准I/O

C标准库中提供了标准I/O(通常简单称作stdio). 其中实现了一个跨平台用户缓冲的解决方案. 但是C语言没有对其他高级功能提供内嵌支持, 也没有对I/O的内在支持.

文件指针

标准I/O例程并不直接操作文件描述符. 取而代之的是他们用自己唯一的标识符, 即文件指针(file pointer).
在标准I/O中, 一个打开的文件叫做"流". 流可以被打开用来读(输入流)写(输出流), 或者两者兼有.

打开文件

文件通过fopen()打开以供读写操作:

#include <stdio.h>
FIFL* fopen(const char * path, const char * mode):

这个函数根据指定的模式打开文件path, 并为它关联一个新的流.

  • r
    打开文件用来读取. 流定位在文件的开始处.
  • r+
    打开文件用来读写. 流定位在文件的开始处.
  • w
    打开文件用来写入, 如果文件存在, 文件会被清空. 如果文件存在, 它会被清空. 流定位在文件开始处.
  • w+
    打开文件用来读写. 如果文件存在, 文件会被清空. 如果文件存在, 他会被创建. 流被设置在文件的开始处.
  • a
    打开文件用来追加模式的写入. 如果文件不存在它会被创建. 流被设置在文件的末尾. 所有的写入都会接在文件后.
  • a+
    打开文件用来追加模式的读写. 如果文件不存在, 他会被创建. 流被设置在文件的末尾. 所有的写入都会接在文件后.

给定的模式可能还有字符b. 但这个值在Llinux下通常会被忽略. 一些操作系统用不同的方式对待文本和二进制文件, 并且b模式指示完美用二进制打开.
成功时, fopen()返回一个合法的FILE指针. 失败时, 它返回NULL, 而且相应的设置errno

通过文件描述符打开文件

函数fdopen()将一个已经打开的文件描述符(fd)转成一个流:

#include <stdio.h>
FILE * fdopen( int fd, const char * mode):

流的位置设置在文件描述符指向的文件位置. 一旦文件描述符被转换成一个流, 则不应该直接在该文件描述符上进行I/O(尽管这么做是合法的). 需要注意的是文件描述符没有被复制, 而只是关联了一个新的流. 关闭流也会关闭相应的文件描述符. 成功时, fdopen()返回一个合法的文件指针. 失败时, 返回NULL.

关闭流

fclose()函数关闭一个给定的流

#include <stdio.h>
int fclose( FILE *stream);

所有被缓冲但还没有被写出的数据会被先写出. 成功时, fclose()返回0. 失败时返回EOF并且相应的设置errno.

关闭所有的流

fcloseall()函数关闭所有的和当前进程相关联的流, 包括标准输出,

#define _GUN_SOURCE
#include <stdio.h>
int fcloseall( void );

关闭之前, 所有的流会被写出. 这个函数始终返回0; 该函数是Linux所特有的.

单字节读取

函数fgetc()可以用来从流中读取单个字符.

#include <stdio.h>
int fgetc (FILE * stream);
把字符回放入流中
#include <stdio.h>
int ungetc ( int c, FILE *stream);
按 行的读取

函数fgets()从一个给定的流中读取一个字符串:

#include <stdio.h>
char * fgets ( char * str, int size, FILE* stream);

这个函数从流中读取size-1个字节的数据, 并把数据存入str中. 当所有字节读入时, 空字符被存入字符串末尾. 当读到EOF或换行符时读入结束. 如果读到了一个换行符, '\n’被存入str.
成果时返回str; 失败时, 返回NULL.

char buf[ LINE_MAX};
if (!fgets (buf, LINE_MAX, stream))
   /*ERROR*/

POSIX在<limits.h>中定义了宏LINE_MAX; 它是POSIX行控制接口能够处理的输入行的最大长度. linux的C函数库没有提供这样的限制(行可以是任意长度), 但是不能和宏LINE_MAX交互.

读取二进制文件

一些应用程序不只是读取字符. 有时候开发者想读写复杂的二进制数据. 因此, 标注输入输出库提供了函数fread():

#include <stdio.h>
size_t fread( void * buf, size_t size, size_t nr, FILE * stream);

调用fread()会从输入输出流中读取nr个数据, 每个数据有size个字节, 并将数据放入到buf所指向的缓冲区. 文件指针向前移动读出数据的长度.

写入单个字符
#include <stdio.h>
int fputc( int c, FILE * stream);
写入字符串
#include <stdio.h>
int fputs ( const char * str, FILE *stream);
写入二进制数据
#include <stdio.h>
size_t fwrite( void *buf, size_t size, size_t nr, FILE * stream);
定位流
#include <stdio.h>
int fseek (FILE * stream, long offset, int whence);

如果whence被设置为SEEK_SET, 文件的位置被设置到offset处. 如果whence被设置为SEEK_CUR, 文件位置被设置到当前位置加上offset. 如果whence被设置SEEK_END, 文件位置被设置到文件末尾加上offset.
成功时, fseek()返回0. 清空文件结束标志, 并且取消ungetc()操作. 错误时, 它返回-1, 并且相应的设置errno. 最常见的错误有非法流(EBADF)和非法whence参数(EINVAL).

#include <stdio.h>
int fsetpos( FILE *stream, fpos_t, * pos);

这个函数将流的位置设置到pos处, 他和将whence设置为SEEK_SET时的fseek() 功能一致. 成功时它返回0, 否则, 返回-1.并且相应的设置errno的值.
标准I/O也提供了rewind()函数, 下面是一个片段:

#include <stdio.h>
void rewind ( FILE * stream);

rewind(stream);
将位置重置到流的初始位置, 它等价于:
fseek(stream, 0, SEEK_SET);
rewind()没有返回值. 因此不能够直接提供错误信息. 调用函数如果希望获取错误, 需要在调用之前清空errno, 并且检查这个变量在调用之后是否非0.

获取当前流的位置

和lseek()不同, fseek() 不返回更新后的位置. 一个单独的接口提供了这个功能. ftell()函数返回当前流的位置.

#include <stdio.h>
long ftell (FILE * stream);

错误时, 他返回-1, 并且相应的设置errno. 选择性的, 标准输入输出提供了fgetspos()函数.

#include <stdio.h>
int fgetpos( FILE *stream, fpos_t *pos);

成功时, fgetpos()返回0, 并且将当前流的位置设置为pos.失败时, 返回-1, 并且相应的设置errno. 和fsetpos()一样, fgetpos()只在有复杂文件位置类型的非linux平台上提供.

清洗一个流

标准I/O库提供了一个用来将用户缓冲区写入内核, 并且保证所有的数据都通过write()写出的函数.

#include <stdio.h>
int fflush (FILE *stream);

调用该函数时, stream指向的流中的所有未写入的数据会被清洗(flush)到内核中. 如果stream是空的(NULL), 所有进程打开的流会被清洗掉. 成功时fflush()返回0. 失败时, 它返回EOF, 并且相应的设置errno.

注意哦. 这里是用户的缓冲区哦
这个缓冲区保留在用户空间中, 并且运行用户的代码, 不执行系统调用. 效率提高的原因.

错误和文件结束

一些标准I/O接口, 例如fread(), 像调用者传递失败信息的能力很差, 因为他们没有提供区分错误和EOF的机制. 在一些场合中调用这些函数时, 需要区分给定的流出现了错误还是到达了文件结尾. 标准I/O提供了两个函数. 函数ferror()测试是否在流上设置了错误标志:

#include <stdio.h>
int ferror (FILE * stream);

错误标志由其他相应错误的标准I/函数设置, 如果标志被设置, 函数返回非零值, 否则返回0.

函数feof()从何处文件结尾标志是否被设置:

#include <stdio.h>
int feof (FILE * stream);

到达文件结尾时, EOF标志会被其他标准I/O函数设置, 如果标志被设置了, 函数非0值, 否则返回0.

clearerr() 函数为流清空错误和文件结尾标志:

#include <stdio.h>
void clearerr ( FILE *stream);

他没有返回值, 而且不会失败(没有方法知道是否提供了一个非法的流) 是有在检查error和EOF标志之后, 你猜可以调用clearerr(), 因为这些操作是不可恢复的.

获取关联的文件描述符

获取流的文件描述符, 可以使用fileno():

#include <stdio.h>
int fileno ( FILE *stream);

成功时, fileno()返回合流相关的文件描述符. 失败时, 他返回-1. 当给定的流非法时才可能发生这种情况. 这个时候函数会将errno设置为EBADF. 通常不建议使用标准I/O和系统调用. 使用fileno()时, 程序员需要确保操作非常严谨. 尤其需要注意的是, 最好在执行获取文件描述符之前对流进行冲洗(flush). 记住, 最好永远不要混合使用I/O操作.

####控制缓冲

标准I/O实现了三种用户缓冲. 下面是一些选项.

  • 不缓冲
    不执行用户缓冲, 数据直接提交到内核, 因为这和用户缓冲对立, 这个选项通常不用. 标准错误默认是不缓冲的
  • 行缓冲
    缓冲以行为单位执行. 每当遇到换行符, 缓冲区被提交到内核. 行缓冲对输出到屏幕的流有用. 因此, 它是终端默认缓冲方式(标准数据默认行为航缓冲).
  • 块缓冲
    缓冲以块为单位执行. 它适用于文件. 默认的所有和文件有关的流都是块缓冲的. 标准I/O称块缓冲为全缓冲.

大部分情况下, 默认的缓冲类型是最高效的, 标准I/O确实提供了一个用来控制使用的缓冲类型的函数:

#include <stdio.h>
int setvbuf ( FILE *stream, char * buf, int mod, size_t size);
  • _IONBF 无缓冲
  • _IOLBF 行缓冲
  • _IOFBF 块缓冲

除了_IONBF情况下buf和size被忽略了. buf可以指向一个size字节大小的缓冲空间. 标准I/O会用它来执行对给定流的缓冲. 如果BUF位空, 缓冲区则由glibc自动分配;
setbuf()函数必须在打开流后, 但在执行任何操作之前被滴啊用. 成功时返回0. 出错返回一个非0值.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值