基础IO+文件(一)

简介: 基础IO+文件

回顾文件


空文件,也是要在磁盘中占据空间

文件=内容+属性

文件操作=内容+属性操作

标识一个文件:文件名+路径

一个文件要被访问必须先打开:先是通过进程调用接口,接着由操作系统去实现

磁盘中存在着打开的文件和未被打开的文件

文件的本质:进程和被打开文件的关系


回顾文件操作


C语言有文件操作接口,其他语言同样也有,并且都不一样

文件保存在磁盘上,磁盘属于硬件,只能通过操作系统进行访问;想要访问文件就绕不开操作系统;操作系统会提供一系列的系统调用接口进行文件访问


但是,操作系统只有一个,语言却存在很多种;所以无论语言是怎么变化的,系统调用的底层是不变化的


库函数调用接口


写文件-w


int fprintf(FILE *stream, const char *format, ...);


先介绍打印函数 fprintf,将内容写到输入流中也就是写到文件中


ed0ccb5a2bdad357aa10c2a49d7d9d23_c5f7842de1274ee994f86a15183fee5a.png

结果如下


42759d5c2946f81f2c1ae0a062837d97_97ec7282d06e4405b070aa7144c8a1e7.png


9e114df5e786df75a614e0eeb843c4c6_e7d812dd022940bcb10e1bf23e3c6e6a.png

结果如下


c61b50dc1e2cae1199dfaf2badd18414_350bbdfb69784cf199e101ab08736d90.png


读文件-r


char *fgets(char *s, int size, FILE *stream);


介绍读取函数 fgets在文件中读取大小为 size的内容到字符串 s中


8df9d46c2707c7bc67f33d879db739e2_e86d258455cf41b5a38187aa07deaa23.png


结果如下


6cec19e560e0642e44f308a709a55e68_569e27398e4f43b9a9e498d268c75e89.png


系统调用


打开文件-open


文件存在时:


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


文件不存在时:


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


返回值


open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).


pathname:文件名/文件路径

flags:标识符:O_RDONLY 只读, O_WRONLY只写,O_CREAT创建,O_APPEND追加,O_TRUNC每次写时把之前的内容清空;宏所定义的;每个宏对应的数值,只有一个比特位,彼此位置不重叠

mode:文件权限

返回值:成功,返回文件描述符;失败,返回-1

由于文件不存在,所以需要先创建O_CREAT;文件权限默认是0666


3f34f18cd5244a46e02e34d8e06787fb_1d9d2e66528f4220a51753e2c49f11df.png


结果如下

由于掩码的存在,文件权限并不是0666


c8a36ffb258aec26178742716d15a2d0_d62f6e977a9b40228dee24662020a3fc.png


写文件-write


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


将大小为 count的内容 buf写到文件中;注意这里的内容类型没有限制,无论是二进制类还是文本类,在操作系统看来都是二进制类;写入成功时返回写入的大小 ssize_t;注意在写入字符串时,这里不同于C语言的规定,不需要计算最后一个字符


1235f855d05a718a3e028f79e8fe6fed_e0b0e3f03ebc491e9bf01f259474af3a.png


结果如下


0554f731096823d9812d0d6ea66b7c49_26b08e946da04cce8192f970068546b6.png


相比于库函数调用的写文件 w就相当于系统调用FILE_NAME,O_WRONLY|O_CREAT|O_TRUNC,0666;所以库函数调用本质是对系统调用的封装,追加,读取类似


e7e268f86cd11fe878241d02e955f18d_55f3c35236f24d1892cea51f5d7c0894.png


文件操作本质


在上面已经说明文件操作的本质是进程和被打开文件的关系,接下来进行深入学习


首先一个进程可以打开多个文件,所以系统中就必然存在着大量被打开的文件,系统便需要对这些文件进行管理,管理方式:先描述再组织;为了管理这些文件,创建对应的内核结构体struct file{ },其中包含文件的大部分属性


在上面系统调用打开文件时,返回的文件描述符是数字3,为什么呢???

再观察下列代码

cf44006f6fdc71cd99067dd613ffde27_497789f23e504e388c6a425c6ab5d111.png

c3c870bb806ab1be5569a896990abf2c_81f864f973934b03a469e30c6db88b2d.png


同时打开三个文件,返回的文件描述符是连续的额3,4,5;为什么是从3开始呢,而且还是连续的呢???


其原因是,每个文件中都存在着三个标准输出流


stdin   键盘
stdout  显示器
stderr  显示器


26571195700321f434cccb03d00156e4_311b10b9c75d489387ecf60cbbb47417.png

它们占据着文件描述符的前三个位置

验证结果如下

a44d198cf9f0a41e7c0538123c850c95_dcc515e808144a49bae7272ac00bb632.png


f5048631779b3fc3649e0c33fed5c302_b05ce2525e954cb6bc17089a4d63d876.png


在系统调用中接受返回值(文件描述符)是使用 int;为什么在标准输出中也会存在着库函数调用接口中接受返回值的 FILE*呢???

其实 FILE本身是一个结构体,是对系统调用的封装,所以其中就有一部分是文件描述符


文件描述符fd


上面还剩一个问题没有解决,就是问什么为返回的文件描述符都是连续的呢???


下面进行图解


6f4b587943a1bb874d65e901504b40f6_2efa2649cc8a4e1abbd1526a2a36c783.png


磁盘中的文件test.txt加载到内存中,系统创建结构体struct file对其进行管理;进程的结构体中存在着一个指向管理所有内存中文件的结构体,次结构体中存在这一个数组,也称进程的文件描述符表从下标为零的位置开始指向标准输出,输入,错误,紧接着就是管理文件的结构体;所以,文件描述符的本质就是数组的下标


文件描述符的分配规则


观察下列代码,当我们把文件描述符fd==0关闭时,结果会怎么样呢


14387b4bb6a08084aa5580ff3867b0ad_31be8403843842dc936306baf7963d9d.png

7427dd15e127ed0248268c012d84ebb1_1655546a27d84f92ab02daf400e116e4.png

如果关闭的是文件描述符fd==2(不是关闭fd==1,原因下面会解释,结果又是怎么样的呢?


337f333598bbc818daab4f49b97f09f2_67134ba45e044719b8fce0e617ec5520.png

0952081fe16ead3f5ddb697de31fcb07_50879b6d24d04cc19fa8eef7a71c5cc4.png


由此便可以得出结论:文件描述符的分配规则是从小到大,按照顺序寻找最小的且没有被占用的文件描述符


如果关闭文件描述符fd==1,结果会怎样呢?


ffe06b20a4a3904af5a616c13d1fa869_729ac71b028b435e9c361cefa1552817.png

ea1ccdb40c41a8bd35591ef3ce278dd4_fe488219ac764b4db0ab2404a38d1219.png

很奇怪!!!为什么是这个结果,不应该是打印数字1吗?

文件描述符fd==1是标准输出,也就是将结果打印到显示器上面,现在它被关闭了结果便是显示器并没有打印结果,很合理;那么本来应该被打印的结果去了哪里呢

下面进行图解


7c7b8596a2d002585735e387f8850930_408c9dd90e8146f5b6ea305da52aac21.png


从图中可以看到,根据文件描述符的分配规则。当fd==1被关闭之后,管理文件的结构体就寻找到该描述符,并将其指向自己;所以原本应该打印到显示器中内容,现在都被打印到了文件中,接下来打开文件进行验证


8a1bcccd412c6fed4a65d11d19b3d403_676d83363b4b48f6afc221321ea40592.png


结果正确,其实这里还存在着缓冲区的问题,在后面会进行学习的;本应该打印到显示器的内容通过关闭描述符,就可以改变其打印的方向,这种操作就称作重定向,下面我们就来学习有关重定向的内容吧


重定向


重定向的本质:上层调用不变的情况下,改变底层的输出方向

上面是通过关闭文件描述符的方式进行重定向,这种方式太低端;这里介绍一种新的凡是进行重定向


int dup2(int oldfd, int newfd);


将文件描述符表中指向旧文件结构体的指针改变方向,指向新的文件结构体中;若成功,返回新的文件描述符


重定向标准输出dup2(fd,2)

代码如下

8b8b009d75e292f5d09759546004895c_a5406fbaec7c41ce8d1393a4b8550541.png

dc88fbfaf831da0bb5c03222f4742e7f_9bd9fa0fb078424184aeadfcaea9ad46.png


图解


fc66c2c7f43791302657c5ace4b1dc00_fc36de23232f4fa0b88a5b0334661c4a.png


重定向追加,标准输入类似,这里就不加赘述


Linux下一切皆文件,这句话该如何理解呢?

在冯诺依曼体系中,操作系统是通过驱动进而控制硬件;每种硬件其实都是由相应的结构体进行控制的,包括其读写功能;上面学习的当文件加载到内存中时会被其对应的结构体进行管理,其实对应的结构体中包含了两个重要的属性读写函数指针;当我们向键盘输入内容时,相应的文件就会调用其函数指针指向键盘结构体对应的功能,由此便可对上面的文件结构体进行补充:每个文件打开时,都会有对应的结构体去进行管理它,同时会调用函数指针完成初始化,并且找到该文件在内核中的缓冲区;所以就在文件结构体的上层来看,所有的设备和文件,统一都是通过调用文件结构体去完成,便可以解释Linux下一切皆文件


图解如下


38dc22b796e72452b65677f7d3b4b00f_a843aa46aefa431c87a4b0496fc13703.png


FILE


观察下列代码

image.png

07ad00b69bf78ae07bfe068abb4634b1_d499b737a68643c9a09c98379e48aef4.png

由结果来看,直接打印到显示器上面时,代码正常;当重定向到文件中时,却有些不同,库函数调用接口都打印了两次,系统调用接口却只打印了一次,这又是为什么呢?


为了解决这个问题,先来学习缓存区的概念



目录
相关文章
|
1月前
|
Java Unix Windows
|
11天前
|
C++
Open3D File Io 文件IO
Open3D File Io 文件IO
|
19天前
|
存储 缓存 Unix
【嵌入式软件工程师面经】Linux文件IO
【嵌入式软件工程师面经】Linux文件IO
21 1
|
22天前
|
存储 安全 Unix
【.Net Core】深入理解IO之文件和目录
【.Net Core】深入理解IO之文件和目录
30 4
|
22天前
|
Java
io读两个文件,生成list 排重后写本地文件(Java)
io读两个文件,生成list 排重后写本地文件(Java)
13 2
|
2天前
使用字节输入流报错 java.io.FileNotFoundException: srcdruid.properties (系统找不到指定的文件。)
使用字节输入流报错 java.io.FileNotFoundException: srcdruid.properties (系统找不到指定的文件。)
6 0
|
28天前
|
Java
|
29天前
|
Java API
文件IO (File对象, 文件读写)
文件IO (File对象, 文件读写)
26 2
|
1月前
|
消息中间件 关系型数据库 Kafka
实时计算 Flink版操作报错之在执行任务时遇到了一个IO错误,具体表现为无法从本地主机(localhost)下载文件,该怎么解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
1月前
|
C++ 数据格式
【C++】C++中的【文件IO流】使用指南 [手把手代码演示] & [小白秒懂]
【C++】C++中的【文件IO流】使用指南 [手把手代码演示] & [小白秒懂]
【C++】C++中的【文件IO流】使用指南 [手把手代码演示] & [小白秒懂]