文章目录

  • C下面的文件接口
    • fopen函数
    • fwrite函数
    • fread函数
    • fseek函数
    • fclose函数
  • 系统调用的文件接口
    • open函数
    • write函数
    • read函数
    • lseek函数
    • close函数
  • 文件描述符
  • 文件描述符和文件流指针的区别

C下面的文件接口

fopen函数

函数原型:

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

参数:

  • path:要打开的文件,带路径
  • mode:以何种方式打开
参数 打开方式
r 只读,文件流指向文件头部
r+ 读写,文件流指向文件的头部
w 只写,如果文件存在,则清空文件开始写,如果文件不存在,则创建文件
w+ 读写,如果文件存在,则清空文件开始写,如果文件不存在,则创建文件
a 追加写,如果文件不存在,则创建文件,从文件末尾开始写
a+ 可读也可追+加写,如果文件不存在,则创建文件,从文件末尾开始写

有+号的,都可以进行读。

返回值:

成功返回文件流指针,失败返回NULL。

fwrite函数

函数原型:

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

功能就是用来写文件的。

参数:

  • ptr:往文件当中写的内容
  • size:定义往文件当中写的时候,一块是多少字节(通常定义为一个字节)。
  • nmemb:准备写多少块
  • stream:文件流指针,准备写到哪个文件当中去

返回值:

返回成功写入到文件当中的块的数量,不是成功写入的字节,而是成功写入的块的个数。
代码验证:

#include <stdio.h>2 #include <string.h>3 int main(){4   FILE *fp = fopen("./b.txt","w+");5   if(fp==NULL){6     perror("fopen");7     return 0;8   }9   const char* str = "hello world";10   ssize_t w_size = fwrite(str,1,strlen(str),fp);                                             11   printf("w_size:%d\n",w_size);12   return 0;13 }

通常设置块的大小为一个字节,那么块的个数参数就可以按照字符串的字节数量进行填充。返回值就相当于是成功写入的字节数量,因为一个块的大小为1。

fread函数

函数原型:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数:

  • ptr:将从文件当中读到的内容保存到ptr准备的空间当中去,这个空间需要程序员提前准备,防止非法访问其他空间。
  • size:定义从文件当中读的时候,一个块是多少字节(通常定义为一个字节)
  • nmemb:期望读多少块
  • stream:文件流指针

返回值:

成功读入的文件块的个数

代码验证:

 #include <stdio.h>2 int main(){3   FILE *fp = fopen("./b.txt","r");4   if(fp == NULL){5     perror("fopen");6     return 0;7   }8 9   char buf[1024] = {0};10   ssize_t r_size = fread(buf,1,sizeof(buf)-1,fp);11   printf("r_size : %d,buf : %s\n",r_size,buf);                                               12   return 0;13 }
ssize_t r_size = fread(buf,1,sizeof(buf)-1,fp);

这个地方为什么是“sizeof(buf)-1”呢?因为我们要预留“\0”的位置,假设我们有三个字节来存放读到的内容,我们当然可以把三个空间都放读到的内容,不会出错,那为什么还要预留“\0”的位置呢?因为不访问这块空间还不会出事儿,一旦访问,就很可能会因为没有结束标志导致程序崩溃。为了保证安全性,防止越界访问造成程序的崩溃,我们预留一个\0的位置。

fseek函数

函数原型:

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

参数:

  • stream:文件流指针
  • offset:偏移量(向后偏移)
  • whence:将文件流指针偏移到什么位置
含义
SEEK_SET 文件头部
SEEK_CUR 当前文件流指针的位置
SEEK_END 文件末尾

返回值:

成功返回0,失败返回-1.

fseek函数是为了移动文件流指针产生的,那在什么情况下需要我们去移动文件流指针呢?我们来看看下面这段代码:

 #include <stdio.h>2 #include <string.h>3 int main(){4   FILE *fp = fopen("./b.txt","w+");5   if(fp==NULL){6     perror("fopen");7     return 0;8   }9 10   const char* str = "hello world";11   ssize_t w_size = fwrite(str,1,strlen(str),fp);
W> 12   printf("w_size:%d\n",w_size);13 14   char buf[1024] = {0};15   ssize_t r_size = fread(buf,1,sizeof(buf) - 1,fp);
W> 16   printf("r_size : %d,buf : %s\n",r_size,buf);                                               17   return 0;18 }

按理说,我们先往文件当中写,再从文件当中读,没什么问题,但是上述代码的执行结果并不是我们想要的。

执行结果:

这是为什么呢?因为我们往文件当中写完内容之后,文件流指针是指向最后的,我们读取的时候,那从最后去读肯定读不到内容啦。于是我们就需要fseek函数去移动文件流指针。

我们通过fseek函数去修改一下上述代码的文件流指针,然后再次打印试试:

#include <stdio.h>2 #include <string.h>3 int main(){4   FILE *fp = fopen("./b.txt","w+");5   if(fp==NULL){6     perror("fopen");7     return 0;8   }9 10   const char* str = "hello world";11   ssize_t w_size = fwrite(str,1,strlen(str),fp);
W> 12   printf("w_size:%d\n",w_size);13 14   fseek(fp,6,SEEK_SET);                                                                      15 16   char buf[1024] = {0};17   ssize_t r_size = fread(buf,1,sizeof(buf) - 1,fp);
W> 18   printf("r_size : %d,buf : %s\n",r_size,buf);19   return 0;20 }~

现在就可以正常打印了。

fclose函数

函数原型:

int fclose(FILE *fp);

打开文件之后,一定记得关闭文件,否则容易造成文件句柄泄漏,也就是内存的泄漏。

系统调用的文件接口

open函数

函数原型:

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

参数:

  • pathname:要打开或者创建的目标文件,需要带路径
  • flags:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行或运算,构成flags。
含义
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读、写打开

上面这三个常量,必须指定一个且只能指定一个。

含义
O_CREAT 若文件不存在,则创建它,需要使用mode选项,来指明新文件的访问权限
O_APPEND 追加写

上面这两个可以多选,与前三个用或连接。

  • mode:当创建一个新文件的时候,指定新创建文件的权限,传递一个八进制的数字。

返回值:

成功则返回新打开的文件描述符,失败返回-1.
我们说在进程创建之初,就默认打开了三个文件,标准输入,标准输出,标准错误。标准输入在C库当中是stdin,在操作系统内核进程部分就是0号文件描述符。

write函数

函数原型:

ssize_t write(int fd, const void *buf, size_t count);

参数:

  • fd:文件描述符
  • buf:将buf指向的内容写入到文件当中去
  • count:期望写多少字节

返回值:

返回则写入的字节数量

read函数

函数原型:

ssize_t read(int fd, void *buf, size_t count);

参数;

  • fd:文件描述符
  • buf:将从文件中读到的内容写到buf指向的空间当中去
  • count:期望读多少字节

返回值:

成功则返回读到的字节数量

lseek函数

函数原型:

off_t lseek(int fd, off_t offset, int whence);

参数:

  • fd:文件描述符
  • offset:偏移量(向后偏移),单位字节
  • whence:偏移的位置
含义
SEEK_SET 文件头部
SEEK_CUR 当前文件流指针的位置
SEEK_END 文件末尾

返回值:

成功返回偏移的位置,单位是字节。失败返回-1.

close函数

函数原型:

int close(int fd);

函数功能:关闭文件描述符。

代码验证上述函数:

#include <stdio.h>2 #include <fcntl.h>3 #include <string.h>4 #include <unistd.h>5 int main(){6   //打开一个文件7   int fd = open("./c.txt",O_RDWR | O_CREAT,0664);8   if(fd == -1){9     perror("open");10     return 0;11   }12 13   //往文件中写14   const char* str = "happyday";15   size_t w_size = write(fd,str,strlen(str));
W> 16   printf("w_size : %d\n",w_size);17 18   //偏移一下19   lseek(fd,0,SEEK_SET);20 21   //从文件中往外读22   char buf[1024] = {0};23   size_t r_size = read(fd,buf,sizeof(buf) - 1);
W> 24   printf("r_size :%d,buf : %s\n",r_size,buf);25 26   printf("fd : %d\n",fd);27   //关闭文件28   close(fd);29   return 0;30 }

执行结果:

文件描述符

文件描述符的值是一个小正数。

我们循环打印文件描述符:

1 #include <stdio.h>2 #include <fcntl.h>3 int main(){4   while(1){5     int fd = open("./c.txt",O_RDWR | O_CREAT,0664);6     if(fd<0){7       perror("open");8       return 0;9     }10     printf("fd : %d\n",fd);                                                                    11   }12   return 0;13 }

执行结果:
循环了十万次。

为什么是十万次呢?这是操作系统的一个软限制,一个进程打开的文件太多了,达到了一个进程打开文件描述符的上限了。

软限制是可以修改的,但是因为硬限制的存在,这个打开个数也并不能无限增加,那硬限制是什么呢?硬限制是操作系统的资源,因为打开文件描述符是需要耗费内存资源的。

可以在/proc/[pid]/fd文件夹下查看文件描述符信息,观察到文件描述符的分配规则是最小未使用原则。

文件是在进程当中打开的,所以文件描述符和进程脱不了关系,在进程的结构体中,有一个描述进程打开文件信息的结构体指针,该指针指向一个结构体,这个结构体有一个指针数组,数组里面存储的指针指向描述文件信息的结构体,而我们所说的文件描述符就是该指针数组的数组元素下标。在描述文件信息的结构体里,有描述文件的名称、文件的大小、文件的权限、文件的所有者、文件的属性、文件在磁盘当中存储的位置等等。

文件描述符和文件流指针的区别

typedef struct _IO_FILE FILE;

从源码的角度来说,文件流指针是一个结构体(struct _IO_FILE),文件流指针对应的结构体是C库定义的,该结构体属于C库的内容,它并不是操作系统内核的东西,文件流指针结构体内的读缓冲区和写缓冲区也是属于C库,不属于操作系统内核的,所以我们在进程控制当中说进程结束的时候会刷新缓冲区,需要用那四种方法去刷新缓冲区。当调用_exit函数去终止进程的时候,会比exit函数少做的其中一件事就是刷新缓冲区,因为_exit函数是系统调用函数,在内核中运行,接触不到缓冲区。

文件流指针结构体当中还有一个东西是int _fileno,该整形变量用来保存文件描述符的数值,保存的就是内核的文件描述符。

【Linux】基础IO1相关推荐

  1. 一键检查LINUX基础环境

    一键检查LINUX基础环境 搞这个脚本的初衷: 每次我部署完环境,都得认真慢慢检查一般,有点费劲,一直想搞个像样的一键检查脚本,这不,可算抽空打个样了. [root@z4 ~]# sh bench.s ...

  2. 小猿圈Linux基础面试题,看看你能答对几道?

    最近身边的很多朋友都在学习linux,从最开始的安装软件都需要百度一天的他们,现在已经成长为了,不需要百度就可以把自己弄懵圈的了,接下来的几天小猿圈linux老师会为大家准备一些实用的linux技巧分 ...

  3. linux基础知识-链接列表

    linux基础知识-链接列表 1. 安装centos 7 1.1 Linux的初识 1.2 centOS 7安装教程 1.3 centOS 7配置ip和网络问题排查 1.4 PuTTY和Xshell远 ...

  4. linux基础知识_压缩—进程管理-网络管理-ftp-nfs-ssh-scp

    linux基础知识_压缩-进程管理-网络管理-ftp-nfs-ssh-scp 1.压缩包管理 gzip .gz格式的压缩包,不打包,分别压缩,原文件消失 bzip2 .bz2格式的压缩包,原文件不会消 ...

  5. 《嵌入式 Linux应用程序开发标准教程(第2版)》——第1章 Linux快速入门 1.1 嵌入式Linux基础...

    本节书摘来自异步社区<嵌入式 Linux应用程序开发标准教程(第2版)>一书中的第1章,第1.1节,作者 华清远见嵌入式培训中心,更多章节内容可以访问云栖社区"异步社区" ...

  6. linux基础概念和个人笔记总结(6)

    防伪码:曾经梦想仗剑走天涯,回过头,却发现还是放不下家的牵挂 后续理论的验证图文详解,我会更新,希望大家可以参考 第十一章 FTP文件传输服务 1.FTP连接 控制连接:tcp21端口,用于发送ftp ...

  7. Linux基础第一周

    Linux基础第一周 命令的格式 command(命令)option(选项)arguments(参数)三部分组成,之间需要空格间隔 options 选项 启用命令的某项功能,从而更改命令的功能 arg ...

  8. 20155301 滕树晨linux基础——linux进程间通信(IPC)机制总结

    20155301 滕树晨linux基础--linux进程间通信(IPC)机制总结 共享内存 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在 ...

  9. 鸟哥的linux 实训教程,鸟哥的Linux基础学习实训教程

    1.理想的Linux上机实践课程 每周一次.每次三小时的学与练 本书所有例题讲解,均经过鸟哥在大专院校实施多年来的测试,对于学生的理解具有相当满意的效果. 2. 提供一致性教学环境 让学习者不再有陌生 ...

  10. 第90节:Java中的Linux基础

    第90节:Java中的Linux基础 linux是装载虚拟机上面的: JDK依赖包: yum install glibc.i686MYSQL依赖包: yum -y install libaio.so. ...

最新文章

  1. 智源出品 | 超大规模智能模型产业发展报告(附下载)
  2. ASP.NET中PostBack和ViewState
  3. python基础(part5)--容器类型之字符串
  4. 【练习】实现一个parse方法(需要实现的效果见内容),方法总结
  5. SharePoint 2013 Workflow - Advanced Workflow Debugging with Fiddler
  6. 中小机房UPS电源及环境多方式在线监控和告警方案
  7. vb.net 教程 3-10 窗体编程 datagridview控件 7 修改单元格
  8. 整蛊小学妹,督促学习的html代码
  9. 云台球型摄像机行业现状调研及趋势分析报告
  10. mysql中round函数使用
  11. 认知空间是什么意思_什么是认知?
  12. 【有利可图网】PS实战系列:PS+SAI把照片制成唯美手绘效果
  13. Leetcode 853 车队
  14. EDK2编译报错,请帮我看看这个是什么错误
  15. 温商机器人企业_16家温商企业上榜“中国民企500强” 青山控股领衔
  16. 基本数据类型在传参中的自顶向下和自底向上;this;访问权限修饰符
  17. arduino编程语言Wiring参考手册API
  18. 数论的一个基础计算器,集成了同余式,逐次平方法,勒让德计算,模M的K次密等内容
  19. 日期和时间范围区间怎么查询
  20. 【Mo 人工智能技术博客】现在最流行的图神经网络库 pytorch geometric 上手教学

热门文章

  1. java批量上传文件_Spring boot 实现单个或批量文件上传功能
  2. GitHub重磅官宣!Java开发环境搭建超全详解
  3. 投资 5 -- 股票卖出的判断核心依据
  4. 基于Java+Swing实现捕鱼达人游戏(含课程报告)
  5. 顺应媒体融合趋势,中科闻歌携手美摄打造数智媒宣
  6. 【系统美化≤风见幽香热门主题≥win7下载】
  7. 雷达系统 学习笔记(八)——合成孔径雷达1
  8. 什么是第四方支付(聚合支付)
  9. Linux Kali
  10. Caputo 分数阶导数的 H2N2 插值逼近 (附Matlab程序)