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

前言:上一章介绍了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请求,又会拷贝一次到指定缓冲区。写输入也是如此,这样就拷贝了两次。有性能损耗。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值