基础I/O总结

引言

对于文件I/O,即打开文件,读文件,写文件这个应该都不太陌生,这篇文章会首先回忆一下C语言中的一些基础I/O,进而引入到Linux下的文件I/O。

C语言中的文件I/O

打开文件

#include<stdio.h>

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

返回值:
FILE* :文件指针
参数:
path:文件路径
mode:打开方式

这里的打开方式一共有六种:

  • r:以只读方式打开
  • r+:以读写的方式打开,写数据的话从文件的起始处开始
  • w:以只写的方式打开,如果文件不存在,则创建该文件
  • w+:以读写的方式打开,如果文件中已有数据,则删除原数据,如果文件不存在,则创建该文件
  • a:以追加的方式写
  • a+:以追加的方式写,如果文件不存在,则创建,如果文件存在,则清空文件开始写

读写数据

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

size_t fwrite(void *ptr,size_t size,size_t nmemb,FILE *stream);
返回值:读取的字节数
参数:
void *ptr:将数据读到哪个地方
size:这个类型的数据大小
nmemb:一共要读多少个这个类型的数据
stream:从哪里读数据
#include<stdio.h>
#include<string.h>

int main()
{
    FILE* fp = fopen("myfile","w");
    if(!fp)
    {
        printf("fopen error\n");
    }
    const char *msg = "hello world\n";
    int count = 5;
    while(count--)
    {
       fwrite(msg,strlen(msg),1,fp);
    }
    fclose(fp);
    return 0;
}

这里写图片描述
调用fwrite创建了一个myfile文件,然后往里面写了五条hello world。

接下来再调用fread函数从myfile文件中读出这五条数据来。
代码如下:

#include<stdio.h>
#include<string.h>

int main()
{
    FILE* fp = fopen("myfile","r");
    if(!fp)
    {
        printf("fopen error\n");
    }
    char buf[1024];
    const char *msg = "hello,bit!\n";

    while(1)
    {
        size_t s = fread(buf,1,strlen(msg),fp);
        if(s > 0)
        {
            buf[s] = 0;
            printf("%s",buf);
        }
        if(feof(fp))
            break;
    }
    fclose(fp);
    return 0;
}

先从myfile文件中将数据读到buf中,然后输出buf。


linux下的文件I/O

操作Linux下的文件,首先要明白文件描述符,因为对文件的任何操作都是通过文件描述符来进行的。

系统默认打开的三个文件描述符:

stdin:标准输入
stdout:标准输出
syrerr:标准错误

在内核中,这三个文件描述符是被就是FILE*类型的。
这里写图片描述
新文件的文件描述符从最低位的不为空的开始累加。

这里用一段代码演示一下:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>

int main()
{
    close(1);//关闭标准输出的文件描述符
    int fd = open("myfile",O_WRONLY|O_CREAT,0666);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("fd:%d\n",fd);
    fflush(stdout);

    close(fd);
    return 0;
}

这里写图片描述

可以发现使用printf函数打印的话,没有输出到标准输出屏幕上,而是写到了myfile这个文件里,就是因为关闭了1这个文件描述符后,我们打开时创建的myflie文件拿到了1这个文件描述符,而printf函数只认文件描述符。

修改文件描述符的函数:

int dup(int oldfd);
int dup2(int oldfd,int newfd);

文件操作函数:

打开文件

int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);

返回值:文件描述符
参数:
    pathname:文件的路径
    flags:打开方式
        1、O_RDONLY:只读打开
        2、O_WRONLY:只写打开
        3、O_RDWR:读写打开
    这三个常量,必须指定一个且只能是一个
        4、O_CREAT:若文件不存在,则创建,需要只能mode来指明新文件的权限
        5、O_ARREND:追加写
    mode:文件的初始权限

关闭文件函数:

int close(int fd);
直接给定要关闭的文件的文件描述符即可

像上图一样,所有的文件描述符都是在一个下标从0开始的数组里,所以是占有内核资源的,如果只打开而不关闭的话,肯定会造成资源浪费,最终导致系统资源耗尽。

文件写入函数:

ssize_t write(int fd,const void *buf,size_t count);
返回值:ssize_t是一个有符号的长整型,写入失败返回-1,写入成功返回写入的字节数
参数:
    fd:所要操作的文件的文件描述符
    buf:将数据往哪里写
    count:一次写入的字节数

文件写入函数

ssize_t read(int fd,void *buf,siza_t count)
返回值:和write函数类似,读数据失败返回-1,成果返回读出的字节数
参数:
    fd:所要操作的文件的文件描述符
    buf:将数据往哪里读
    count:一次读出的字节数

最后再来看一个现象总结一下:

#include<stdio.h>


int main()
{
    const char *msg1 = "Hello,printf!\n";
    const char *msg2 = "Hello,fwrite!\n";
    const char *msg3 = "Hello,write!\n";

    printf("%s",msg1);
    fwrite(msg2,strlen(msg2),1,stdout);
    write(1,msg3,strlen(msg3));
    fork();
    return 0;
}

同时使用printf,fwrite,write三个函数往标准输出屏幕上写一句话。
这里写图片描述
发现输出到标准输出上没毛病,就是三条结果。

再让他们输出到文件里看下效果:
这里写图片描述
输出到文件里竟然变成了五条结果:
而且发现是printf个fwrite两个输出了两次,解释一下:

  1. 标准输出采用的是行缓冲机制
  2. 文件内采用的是全缓冲机制
  3. write是立即输出,不会经过缓冲区
  4. 子进程会拷贝父进程的缓冲区

当写入文件内时,由于文件采用全缓冲机制,所以printf和fwrite两条输出在缓冲区里还有一份,而fork出来的子进程会拷贝父进程的缓冲区,所以这两条打印了两次。

以上就是关于文件I/O的一点小小总结。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值