【Linux】基础IO1
文章目录
- 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相关推荐
- 一键检查LINUX基础环境
一键检查LINUX基础环境 搞这个脚本的初衷: 每次我部署完环境,都得认真慢慢检查一般,有点费劲,一直想搞个像样的一键检查脚本,这不,可算抽空打个样了. [root@z4 ~]# sh bench.s ...
- 小猿圈Linux基础面试题,看看你能答对几道?
最近身边的很多朋友都在学习linux,从最开始的安装软件都需要百度一天的他们,现在已经成长为了,不需要百度就可以把自己弄懵圈的了,接下来的几天小猿圈linux老师会为大家准备一些实用的linux技巧分 ...
- linux基础知识-链接列表
linux基础知识-链接列表 1. 安装centos 7 1.1 Linux的初识 1.2 centOS 7安装教程 1.3 centOS 7配置ip和网络问题排查 1.4 PuTTY和Xshell远 ...
- linux基础知识_压缩—进程管理-网络管理-ftp-nfs-ssh-scp
linux基础知识_压缩-进程管理-网络管理-ftp-nfs-ssh-scp 1.压缩包管理 gzip .gz格式的压缩包,不打包,分别压缩,原文件消失 bzip2 .bz2格式的压缩包,原文件不会消 ...
- 《嵌入式 Linux应用程序开发标准教程(第2版)》——第1章 Linux快速入门 1.1 嵌入式Linux基础...
本节书摘来自异步社区<嵌入式 Linux应用程序开发标准教程(第2版)>一书中的第1章,第1.1节,作者 华清远见嵌入式培训中心,更多章节内容可以访问云栖社区"异步社区" ...
- linux基础概念和个人笔记总结(6)
防伪码:曾经梦想仗剑走天涯,回过头,却发现还是放不下家的牵挂 后续理论的验证图文详解,我会更新,希望大家可以参考 第十一章 FTP文件传输服务 1.FTP连接 控制连接:tcp21端口,用于发送ftp ...
- Linux基础第一周
Linux基础第一周 命令的格式 command(命令)option(选项)arguments(参数)三部分组成,之间需要空格间隔 options 选项 启用命令的某项功能,从而更改命令的功能 arg ...
- 20155301 滕树晨linux基础——linux进程间通信(IPC)机制总结
20155301 滕树晨linux基础--linux进程间通信(IPC)机制总结 共享内存 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在 ...
- 鸟哥的linux 实训教程,鸟哥的Linux基础学习实训教程
1.理想的Linux上机实践课程 每周一次.每次三小时的学与练 本书所有例题讲解,均经过鸟哥在大专院校实施多年来的测试,对于学生的理解具有相当满意的效果. 2. 提供一致性教学环境 让学习者不再有陌生 ...
- 第90节:Java中的Linux基础
第90节:Java中的Linux基础 linux是装载虚拟机上面的: JDK依赖包: yum install glibc.i686MYSQL依赖包: yum -y install libaio.so. ...
最新文章
- 智源出品 | 超大规模智能模型产业发展报告(附下载)
- ASP.NET中PostBack和ViewState
- python基础(part5)--容器类型之字符串
- 【练习】实现一个parse方法(需要实现的效果见内容),方法总结
- SharePoint 2013 Workflow - Advanced Workflow Debugging with Fiddler
- 中小机房UPS电源及环境多方式在线监控和告警方案
- vb.net 教程 3-10 窗体编程 datagridview控件 7 修改单元格
- 整蛊小学妹,督促学习的html代码
- 云台球型摄像机行业现状调研及趋势分析报告
- mysql中round函数使用
- 认知空间是什么意思_什么是认知?
- 【有利可图网】PS实战系列:PS+SAI把照片制成唯美手绘效果
- Leetcode 853 车队
- EDK2编译报错,请帮我看看这个是什么错误
- 温商机器人企业_16家温商企业上榜“中国民企500强” 青山控股领衔
- 基本数据类型在传参中的自顶向下和自底向上;this;访问权限修饰符
- arduino编程语言Wiring参考手册API
- 数论的一个基础计算器,集成了同余式,逐次平方法,勒让德计算,模M的K次密等内容
- 日期和时间范围区间怎么查询
- 【Mo 人工智能技术博客】现在最流行的图神经网络库 pytorch geometric 上手教学