Linux 系统编程2:缓冲I/O(标准I/O)
创始人
2024-12-08 23:03:17

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

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

1.打开文件

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

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

2.从流中读取数据:

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

#include    int fgetc(FILE* stream); 

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

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

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

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

3:从流中写数据:

1).写入单个字符:

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

2).写入一行;

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

3).写入二进制数据。

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

例子:

#include  #include  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<

4.Flush(刷新输出流)

#include   int fflush(FILE* stream);

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

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

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

相关内容

热门资讯

总投资630亿!京东方第8.6... 快科技5月14日消息,据报道,京东方计划于本月底在其成都的B16工厂开始量产第8.6代OLED面板。...
工业称重传感器选型指南:精度与... 工业称重传感器选型指南:精度与可靠性的平衡之道 工业称重传感器选型指南:精度与可靠性的平衡之道 一、...
医学院硕博研究生靠虚开发票冒签... 33岁的柴某系江苏人,案发前系某医院博士后研究人员。一审法院认定,2018年至2023年,柴某在某医...
AI服务器、先进封装及HBM技... 据财联社消息,近来,AI服务器、先进封装及HBM技术迭代带动硅片与电子特气用量激增,叠加中东能源危机...
科幻走进现实!首款载人变形机甲... 你会怎么称呼视频里的炫酷机甲?是科幻片里看的“钢铁巨兽”,还是现实版的“变形金刚”? 5月12日,中...