Linux 系统编程2:缓冲I/O(标准I/O)

avatar
作者
筋斗云
阅读量:0

前言:上一章介绍了LINUX系统调用的一些文件I/O函数,本章将继续学习C库中的标准I/O函数。

文件指针:标准I/O并不是直接操作文件描述符,他们有自己的唯一标识符---文件指针去操作.文件指针和文件描述符是一一映射的关系

1.打开文件

   1).  通过fopen()打开,返回文件所映射的文件指针。

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

和open()函数类似,有文件路径和打开方式。mode有如下几种:
r:只读模式打开文件,流指针指向文件开始。

r+:可读模式打开,流指针指向文件开始。

w:只写模式打开,往里写东西,若文件存在,文件内容被清空,文件不存在则创建文件,流指针指向文件开始。

w+:可写模式打开文件,规则情况同w

a:追加模式打开文件,文件不存在则创建文件。流指针指向文件的末尾,所有人写的文件都是追加到文件的末尾。

FILE* stream; stream=fopen("/etc/manifest","r"); //只读模式打开

2).可以通过函数fdopen()把一个已经打开的文件描述符(open()或网络编程函数创建的可以进行读写的文件描述符)转化为流。

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

  但要注意的是:fdopen()的可能模式和fopen()的可能模式相同,而且必须和最初打开文件描述符的模式相匹配。fdopen()里指定的写模式不会清空原文件内容。

注意文件描述符并没有被复制,而只是关联了一个新的流,关闭了流也会关闭文件描述符。如下例子。

FILE* stream; int fd;  fd=open("/home/user/1.txt",O_RDONLY); if(fd==-1){ ........ }  stream=fdopen(fd,"r"); if(!stream)      /*error*/

3).关闭流

#include <stdio.h>  int fclose(FILE* stream);  int fcloseall(void);//此函数会关闭所有当前进程的输入输出流,包括标准输入和标准输出。

2.从流中读取数据:

1). 一般情况下是每次只读取一个字符。函数fgetc()可以每次从流中读取单个字符;

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

注意上述函数强转为unsigned int类 不论读取什么类型的字符。比如char类,想输出强转回去。

int c; c=fgetc(stream);  printf("c=%c\n",(char)c);

2).每次读取一行的数据,函数fgets()

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

该函数从stream中读取size-1个字节,保存在str中,读取最后一个字节后,缓冲区会写入'\0',读到EOF或换行符时,读结束,换行符'\n'写入str中。 读取成功后返回str,失败是NULL。

char buf[MAX_SIZE];   if(!fgets(buf,MAX_SIZE,stream) /*error*/

3).读取二进制文件。

#include <stdio.h>  size_t fread(void* buf,size_t size,size_t nr,FILE* stream);  从流stream中,读取nr项数据,每项size个字节,存放在buf中 返回实际读到的项数,文件指针向前移动读到的字节数。

3:从流中写数据:

1).写入单个字符:

#include <stdio.h> int fgetc(int c,FILE* stream);  文件指针stream必须以写打开,会将c所表示的字节强转为unsigned char类存入流中,成功返回c,失败返回EOF。

2).写入一行;

#include <stdio.h>  int fputs(const char* str,FILE* STREAM)  将指向str的所有字符串全部都写入stream中,不会写入结束标识符,成功返回非负数

3).写入二进制数据。

#include <stdio.h>  size_t fwrite(void* buf,size_t size,size_t nr,FILE* stream);  会把buf中的nr项,每项size长度的文件将数据写入stream,文件指针向前移动写入字节数

例子:

#include <stdio.h> #include <iostream> int main(){     FILE* in ,*out;     struct pirate{         char mame[100];         long booty;         int beard_len;     }p,blackbeard{"Edward Teach",950,48};      out=fopen("hello.txt","w");     if(!out){         perror("fopen");         exit(1);     }      if(!fwrite(&blackbeard,sizeof(struct pirate),1,out)){         perror("fwrite");         exit(1);     }      fclose(out);     in=fopen("hello.txt","r");     if(!in){         perror("fopen");         exit(1);     }      if(!fread(&p,sizeof(struct pirate),1,in)){         perror("fread");         exit(1);     }     fclose(in);     std::cout<<p.beard_len<<p.booty<<p.mame<<std::endl;     return 0;     }

4.Flush(刷新输出流)

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

调用该函数时,stream指向的流所有未使用的数据会被flush到内核中。

本节中所以调用所需要的缓冲区都是有C库函数来维持的,其处在用户空间,而不是内核空间。这些调用的性能提升空间来自于用户空间,而不是内核,不是系统调用。fflush的作用是,将用户没写的数据加入内核缓冲区中,类似于直接调用write函数。但其不能保证数据最终被写入磁盘中,需要先调用fflush()后立刻调用fsync().先确保被写入内核当中,然后保证内核数据写入磁盘中。

这里提一点:标准I/O其实性能比较低,当读数据时,标准I/O会向内核发起read()系统调用,将数据拷贝在标准I/O缓存中,然后通过标准I/O的fget请求,又会拷贝一次到指定缓冲区。写输入也是如此,这样就拷贝了两次。有性能损耗。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!