apue(三)、标准IO

200 阅读5分钟

1、标准IO和系统IO的区别

类型可移植性实时性吞吐量功能
STDIO受限
SYSIO自由

之前学过的系统IO是由内核直接提供的函数库实现的,不同的操作系统平台上提供的IO操作接口不同。系统IO操作都是围绕文件描述符展开的,而对于标准IO是围绕流(stream)进行的。当用标准IO库打开或创建一个文件时,就使得一个流与一个文件相关联。

其次标准IO能够使用用户自定义的缓冲区,具体可以分为行缓冲、全缓冲、无缓冲。由于标准IO操作缓冲区的灵活性,使得它相比于系统IO吞吐量更高,但系统IO实时性更好。

标准IO对于驻留在磁盘上的文件通常是全缓冲,当流涉及终端时采取行缓冲。全缓冲只有缓冲区满了之后才会刷新到内核中,行缓冲在遇到换行符时就能会刷新到内核。缓冲区中的数据也可以通过fflush主动刷新到内核中。

#include <stdio.h>

int fflush(FILE *stream);

标准IO能够和系统IO相互转换:

//文件描述符转化为具体的流指针
FILE *fdopen(int fd, const char *type);

//流指针转化为文件描述符
int fileno(FILE *stream);

2、标准IO进行读写

2.1 fopen

 //打开指定路径的文件,返回文件流指针
 FILE *fopen(const char *path, const char *mode);

参数列表:

path:文件路径

mode:打开文件的方式,共计6种,分别是:

  • r:以只读的方式打开文件,并且文件位置指针会被定位到文件首。如果要打开的文件不存在则报错。

  • r+:以读写的方式打开文件,并且文件位置指针会被定位到文件首。如果要打开的文件不存在则报错。   

  • w:以只写的方式打开文件,如果文件不存在则创建,如果文件已存在则被截断为 0 字节,并且文件位置指针会被定位到文件首。

  • w+:以读写的方式打开文件,如果文件不存在则创建,如果文件已存在则被截断为 0 字节,并且文件位置指针会被定位到文件首。

  • a:以追加的方式打开文件,如果文件不存在则创建,且文件位置指针会被定位到文件最后一个有效字符的后面(EOF,end of the file)。

  • a+:以读和追加的方式打开文件,如果文件不存在则创建,且读文件位置指针会被初始化到文件首,但是总是写入到最后一个有效字符的后面(EOF,end of the file)。

2.2 fgetc/fgets

#include <stdio.h>

int fgetc(FILE *stream);

char *fgets(char *s, int size, FILE *stream);

fgets 并没有解决 gets 函数会导致缓冲区溢出的问题,而是指定缓冲区字节长度来避免多读入数据。当遇到以下四种情况时会返回:(1)数据读入量达到 n-1;(2)读入字符遇到 \n时;(3)读入字符遇到EOF时;(4)读取遇到错误时; 它会在读取到的数据的最后面添加一个 \0 到s中。

2.3 fread/fwrite

 #include <stdio.h>
 
 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

参数列表:

  • ptr:fread(3) 将从 stream 中读取出来的数据回填到 ptr 所指向的位置;fwrite(3) 则将从 ptr 所只想的位置读取数据写入到 stream 中;

  • size:要读取的每个对象所占用的字节数;

  • nmemb:要读取出多少个对象;

  • stream:数据来源或去向;

注意 fread/fwrite 分别返回成功读写对象的个数,而不是字节数。这两个函数有以下两种常见的用法:

(1)读或写一个二进制数组。

float data[10];

if (fwrite(&data[2], sizeof(float), 4, fp) != 4)

    err_sys("write error")

(2)读或写一个结构体

struct {
    short count;
    long total;
    char name[MAXSIZE];
} item;

if (fwrite(&item, sizeof(item), 1, fp) != 1)
   
    err_sys("write error")

2.4 fseek/ftell

 int fseek(FILE *stream, long offset, int whence);
 
 long ftell(FILE *stream);
 
 void rewind(FILE *stream);

ftell用于返回当前文件指针的偏移量,fseek定位一个文件指针的位置,rewind将文件指针定位到文件开头。

3、临时文件

#include<stdio.h>

char *tmpnam(char *ptr);

FILE *tmpfile(void);

tmpname 函数产生一个与现有文件名不同的一个有效路径名字符串,每次调用都将产生一个不同的路径。

tmpfile 函数创建一个临时的二进制文件,在关闭该文件或者程序结束时将自动删除这种文件。UNIX 对二进制文件不进行特殊区分。

tmpfile 常用的标准UNIX技术是先调用 tmpnam 产生一个唯一路径名,然后用该路径名创建一个文件,并立即 unlink 它。因为对于一个文件 unlink 并不会删除其内容,关闭该文件时才会删除其内容。

int main(void) {
   char name[L_tmpnam], line[MAXLINE];
   FILE *fp;
   
   printf("%s\n", tmpnam(NULL));  
   tmpnam(name);
   if ((fp = tmpfile()) == NULL) 
       err_sys("tmpfile error");
   
   fputs("one line of output\n", fp);
   rewind(fp);
   
   if (fgets(line, sizeof(line), fp) == NULL)
       err_sys("fgets error");
       
   fputs(line, stdout);
   exit(0);
}
#include <stdlib.h>

char *mkdtemp(char *template);

int mkstemp(char *template);

mkdtemp 创建临时目录并返回指向目录名的指针;mkstemp 创建临时目录并返回指向目录名的文件描述符。