文件IO

一 文件类型

在Linux系统的设定中,所有可以操作的东西都是文件。所以,Linux将所有的文件分为了七大类别。

类别符号
普通文件-
目录文件d
链接文件l
管道文件p
字符设备文件c
块设备文件b
网络设备文件s

二 系统IO与标准IO

既然万物皆文件,那么对系统的操作就是对文件的操作。

Linux将对文件的操作方法分成了两种类型,分别是:
① 系统IO:由操作系统直接提供的接口函数
② 标准IO:由标准C库(第三方库)提供的接口函数(通过封装操作系统提供的系统IO,再给用户使用)

它们两者的区别可以翻阅下面的博客:
Linux中的系统IO与标准IO

对于新手而言,应该先系统学习系统IO的编程方法,这会有助于以后理解别人封装好的标准IO。

三 操作类型

正常来说,对文件的操作都离不开下面这四种操作类型:

打开文件,读取文件,修改文件,关闭文件。

在系统IO中,系统帮我们把这四种操作类型都进行了封装,分别以单一的单词作为函数名。

操作类型函数名
打开文件open
读取文件read
修改/写 文件write
关闭文件close

四 操作文件

下面将通过调用上面四个操作类型函数来完成对文件的操作。

使用这些函数之前,都需要为其添加头文件。但是,在很多时候我们都会忘记需要添加哪些头文件,这时候可以通过终端调用man语句来查阅该函数的相关书籍。
例如,查阅open函数:# man 2 open,可以看到,使用open函数需要添加: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>这三个函数。如果没有添加这些函数,编译时将产生警告。
偶尔在书籍2中会查不到该函数的相关内容,可以通过修改man后书籍的数字修改章节。具体章节内容可翻阅该博客:
Linux中man命令的使用方法再解释

1、打开文件

在目录中,新建一个openfile.c文件,一个myfile.txt文件。在openfile.c中编写代码如下:

#include"stdio.h"
/* open 所需头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void){
	int open_myfile_fd = open("./myfile.txt",O_RDONLY);
	
	if(open_myfile_fd == -1){
		perror("无法打开该文件,因为");
		return -1;
	}
	
	printf("打开成功\n");
	return 0;
}

编译文件并运行可执行文件,发现系统打印了“打开成功”的字样。

此时,将myfile.txt文件删除,再次运行可执行文件,发现系统打印了错误信息“无法打开该文件,因为: No such file or directory”。

这说明:
调用open函数,系统会到open函数中第一个参数,也就是我们指定的位置找到指定的文件名,然后尝试打开,当该文件不存在或无法打开时,open函数会返回一个“-1”值。

可见,open函数是有两个参数的,而第一个参数的含义就是“路径+文件名”。open函数也是带返回值的,而“-1”代表执行错误的意思,当open函数正常执行时,返回值为正数。

其实,在系统IO中,很多函数都是有返回值的,而返回值“-1”都代表着执行错误的意思。这有助于我们及时发现程序的错误,并制止错误程序继续执行。所以在 if( 返回值 == -1 ) 条件为真之后,通常都会写上 return 语句,将程序终止。

那么,第二个参数是什么意思呢?

open函数的第二个参数代表着对即将打开的文件的操作权限。它有三个不同的值可选,分别为:O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写)。我们可以根据需要修改这个值。

除此之外,open函数还有其他一些参数,对于新手来说可能用不到,如果需要,可以翻阅该博客:linux open()函数各参数说明

2、读取文件

重新创建一个myfile.txt文件,在其中写下任意内容,如“Hello world”。新建一个readfile.c文件,编写代码如下:

#include"stdio.h"
/* open */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* read 所需头文件 */
#include <unistd.h>

int main(void){
	int open_myfile_fd = open("./myfile.txt",O_RDONLY);
	int read_myfile_fd ;
	char content_myfile[50];
	
	if(open_myfile_fd== -1){
		perror("无法打开该文件,因为");
		return -1;
	}
	
	read_myfile_fd = read(open_myfile_fd,content_myfile,50);
	if(read_myfile_fd == -1){
		perror("无法读取该文件,因为");
		return -1;
	}
	printf("%s\n",content_myfile);
	
	return 0;
}

编译该文件并执行可执行文件,可以看到终端打印了“Hello world”的字样。

此时,修改代码,将open函数的第二个参数改为 O_WRONLY。编译并执行可执行文件,可以发现终端打印了“无法读取该文件,因为: Bad file descriptor”的字样。

这说明:
① read函数确实能够读取到所打开文件里面的内容;
② read函数也有返回值,无法读取文件时会返回“-1”值;
③ read函数的执行,需要open函数的可读权限。

那怎样理解read函数呢?

read函数有三个参数需要传入。

第一个参数,是指定调用open函数的返回值。上文讲到,如果open函数正常执行,将返回一个正数,其实,该数值就是被打开文件的入口地址。而read函数需要文件的入口地址,来读取文件里面的内容。除此之外,还需要该入口的相关权限。如果该权限为只写,那么将无法读取文件里面的内容。只有该权限为只读,或者可读写时,才可以读取到文件里面的内容。

第二个参数是将读取到的内容,存储到指定的地点。这里声明了一个字符数组,来存放读取到的信息。

第三个参数是读取内容的长度,可以理解为读取多少内容。这里不用担心内容不够多的问题。

最后还有一个隐藏的知识点,这是向老师询问后才知道。那就是read函数有记录功能。假定被读取的文件中有如下内容:ABCDEFG。
此时我们通过调用read函数,读取三个字节的内容,那么将读取到:ABC。
读取完之后,我们再一次调用read函数,再读取三个字节的内容,那么将会读到:DEF。
这就是read函数隐藏的记录功能,它会自动记住偏移量,方便我们向下读取内容。

3、修改文件

删除原有的myfile.txt文件,重新创建一个新的myfile.txt文件,并新建一个writefile.c文件,编写代码如下:

#include"stdio.h"
/* open */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* write 所需头文件 */
#include <unistd.h>

int main(void){
	int open_myfile_fd = open("./myfile.txt",O_WRONLY);
	int write_myfile_fd ;
	char content_myfile[11] = "HELLO WORLD";
	
	if(open_myfile_fd== -1){
		perror("无法打开该文件,因为");
		return -1;
	}
	
	write_myfile_fd = write(open_myfile_fd,content_myfile,11);
	if(write_myfile_fd == -1){
		perror("无法写入该文件,因为");
		return -1;
	}
	return 0;
}

编译该文件并执行可执行文件。执行可执行文件后,终端并无任何输出。打开myfile.txt文件,发现里面多了一句“HELLO WORLD”的内容,说明此时程序运行正常。

write函数与read函数有很多相似的地方。
① 需要权限。需要修改的文件被open打开的权限需为:O_WRONLY 或者 O_RDWR;
② 需要指定内容的地址。
③ 需要指定写入的数据长度。

第③点有坑,容易出现类似乱码的情况。当给write的第三个参数的数值大于实际所写内容的长度时,最终打开myfile.txt文件会看到内容最后出现类似“\00”的数据。所以,一般最好的做法是以strlen(content_myfile)作为数据长度。

④ write具有记忆功能。

我们修改一下代码,新增写入一段数据。

#include"stdio.h"
/* open */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* write 所需头文件 */
#include <unistd.h>
/* strlen 所需头文件 */
#include <string.h>


int main(void){
	int open_myfile_fd = open("./myfile.txt",O_WRONLY);
	int write_myfile_fd ;
	char content_myfile_1[11] = "Hello world";
	char content_myfile_2[11] = "HELLO WORLD";
	
	if(open_myfile_fd== -1){
		perror("无法打开该文件,因为");
		return -1;
	}
	
	write_myfile_fd = write(open_myfile_fd,content_myfile_1,strlen(content_myfile_1));
	if(write_myfile_fd == -1){
		perror("无法写入该文件,因为");
		return -1;
	}
	write_myfile_fd = write(open_myfile_fd,content_myfile_2,strlen(content_myfile_2));
	if(write_myfile_fd == -1){
		perror("无法写入该文件,因为");
		return -1;
	}
	
	return 0;
}

编译该文件并执行可执行文件。再次打开myfile.txt文件,发现里面多了“HELLO WORLD”内容。

这说明,write函数被多次调用后,会将新增内容追加到已写内容后面,而不会覆盖原来旧的内容。

4、关闭文件

关闭文件是最简单的操作,只需要调用close函数,传入被open打开的文件的fd即可:

/* close 所需头文件 */
 #include <unistd.h>

// close(int fd);
close(open_myfile_fd);

五 总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金三亲

用现在的金钱换取未来的金钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值