标准I/O库总结(standard I/O library)

打开与关闭文件指针

要使用标准I/O库的第一步就是获得文件描述结构FILE*。前两个函数分别根据文件路径和文件描述符打开文件。第三个函数将会根据文件路径打开文件并将其与第三个参数绑定在一起。如果第三个参数之前有关联文件,该文件将被关闭(close)。这个函数的作用有点像dup2,可以将stdin/stdout/stderr关联到指定文件。
       #include 

       FILE *fopen(const char *path, const char *mode);

       FILE *fdopen(int fd, const char *mode);

       FILE *freopen(const char *path, const char *mode, FILE *stream);

关闭文件相对简单,只有一个函数。

int fclose(FILE *fp);

打开模式

在上面的函数中有一个mode参数。该参数决定了文件是否以读(r)、写(w)、追加(a)方式打开。在追加方式(append)下所有的数据将写在文件末尾,类似于open()函数的O_APPEND参数。 该方式主要用于多个进程/线程写同一文件。
以上模式均可以搭配一个“+”(例如r+, w+, a+)。作用是增加读或写权限。例如r+相比于r增加了写权限,w+相比于w增加了读权限。 对于w,w+,a,a+模式,当文件不存在时将被自动创建。

错误处理

对于一个健壮的程序,好的错误处理时必不可少的。尝试获取文件指针后应当检查是否成功并尽可能打印错误信息。当错误发生时errno将会设置,因此可以使用下面的程序段打印错误信息。

if (!fp) {
    perror(NULL);
}


文件缓冲

下图简单描述了标准I/O的函数调用结构。对于每一个文件指针,标准I/O分别包含一个写缓冲和一个读缓冲。当用户程序调用fscanf()或fprintf()读写文件时,数据会先储存在缓冲或从缓冲读取数据。标准I/O支持三种不同的缓冲方式:

1. 无缓冲(unbuffered)。所有的读写操作将直接导致系统调用read/write。
2. 行缓冲(line buffered)。写数据将先存储在写缓冲中直到遇到newline字符或者缓冲满,再调用write。一个特别情况是: 当用户程序调用读函数(例如fgets())从一个无缓冲或者行缓冲的文件读取数据,并且导致read系统调用时,所有在行缓冲(写缓冲)中的数据都将被write。这个特例的合理性在于当程序需要用户输入命令的时候,应当先把提示输出给用户。例如程序输出"please input your name",则这句话应当在fgets之前输出给用户,否则用户怎么知道要输入什么。 对于读缓冲,个人认为行缓冲没有意义,等同于完全缓冲。
3. 完全缓冲(fully buffered)。写数据将先存储在写缓冲中直到缓冲满,再调用write。

标准I/O库总结(standard I/O library)_第1张图片

设置文件缓冲

下面的函数用于改变文件缓冲的大小和类型。前三个函数可以用第四个函数实现。
       #include 

       void setbuf(FILE *stream, char *buf);

       void setbuffer(FILE *stream, char *buf, size_t size);

       void setlinebuf(FILE *stream);

       int setvbuf(FILE *stream, char *buf, int mode, size_t size);

强制刷缓冲

使用fflush()函数可以强制刷读写缓冲。对于读缓冲,所有未被用户程序读取的缓冲数据将被清除。对于写缓冲,所有缓冲数据将被write。当参数stream为空时,该函数将作用于所有写缓冲。
      int fflush(FILE *stream);

缺省缓冲模式

如果stdin或stdout是终端设备,则缺省缓冲模式是行缓冲。
stderr的缺省缓冲模式是无缓冲。
对于普通文件,缺省缓冲模式(非stderr)是完全缓冲。

读数据

按字符读取(Character-at-a-time I/O)

下面的函数用于从指定的文件指针或stdin读取字符。 注意:返回值为int不是char。 这样的目的是可以返回一个不代表任何字符的EOF用于表示错误或文件结束。
      int fgetc(FILE *stream);
      int getc(FILE *stream);
      int getchar(void);

ungetc()函数可以将一个已经读取的字符放回文件。调用该函数的目的主要是将文件指针减一。返回EOF代表错误。

              int ungetc(int c, FILE *stream);

按行读取(Line-at-a-time I/O)

下面的函数用于读取一行或size长度的字符。 由于存在缓冲区越界的风险,不要使用gets()。  当一整行被读取时,newline也被存储在字符串中。
       char *fgets(char *s, int size, FILE *stream);
       char *gets(char *s);

直接I/O(Direct I/O)

直接I/O类似于系统调用read,将多块二进制数据读入缓冲ptr。size表示数据的块数,nmemb代表每块数据的大小。返回值为读取的数据块数,可能小于size。
       size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

错误处理

从以上函数的声明可以发现,无论是发生错误还是文件结束,返回值都是EOF(按字符读取)或者NULL(按行读取)或者读取的数据块数(直接I/O),并且需要注意的是errno没有设置。要具体判断是否发生错误,需要使用feof()或者ferror()函数。clearerr()的作用是清除文件的EOF和error标志。 由于这两个标志只能被clearerr()清除,所以在判断feof()或ferror()后应当清除标志,否则即使文件指针被重新移动到起始,EOF标志一样会存在。
       int feof(FILE *stream);
       int ferror(FILE *stream);
       void clearerr(FILE *stream);


写数据

按字符写

返回EOF表示错误。
       int fputc(int c, FILE *stream);
       int putc(int c, FILE *stream);
       int putchar(int c);

按行写

返回EOF表示错误。
       int fputs(const char *s, FILE *stream);
       int puts(const char *s);

直接I/O

返回写入的数据块数目。
       size_t fwrite(const void *ptr, size_t size, size_t nmemb,
                     FILE *stream);

错误处理

由于写操作不存在文件结束的情况,所以当返回EOF或者返回的数据块数目不等于输入时代表错误发生。

操作文件指针

获取文件指针位置

返回-1代表错误。
       long ftell(FILE *stream);

设置文件指针位置

返回-1代表错误。whence可以设置为SEEK_SET,SEEK_CUR或SEEK_END,分别表示offset是相对于文件起始,文件当前位置或文件结束。rewind()用于将文件指针重置到起始位置,等价于(void) fseek(stream, 0L, SEEK_SET)。
       int fseek(FILE *stream, long offset, int whence);
       void rewind(FILE *stream);

错误处理

所有与文件指针相关的函数返回-1代表错误,并且errno将会设置。

格式化输入输出(formated I/O)

下面的函数解释详见手册。

       int fscanf(FILE *stream, const char *format, ...);
       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);

错误处理

对于格式化输入,返回值的可能情况如下:
1. 在第一次匹配成功或错误前文件结束,返回EOF。
2. 文件读取错误,返回EOF并设置errno。
3. 其他情况, 返回值代表成功匹配并赋值的变量数目(注意含有赋值两字,因为conversion specification支持"*",有可能匹配了但是并不对任何变量赋值)。

对于格式化输出,返回值代表输出的字符数(不计\0)。返回负值代表错误。

其他

某些时候可能需要获取文件结构的底层细节信息,例如对应的文件描述符、读写缓冲区大小等等。

获取文件描述符。
int fileno(FILE *stream);

获取文件缓冲区大小。

size_t __fbufsize(FILE *stream);
获取写缓冲区中的数据大小。
size_t __fpending(FILE *stream);

是否行缓冲

int __flbf(FILE *stream);
是否可读写
int __freadable(FILE *stream);
int __fwritable(FILE *stream);

清除读写缓冲区内的所有数据

void __fpurge(FILE *stream);








你可能感兴趣的:(Linux)