品牌官方网站建设,十堰市茅箭区建设局网站,网络整合营销的含义,计算机应用技术好就业吗✨✨ 欢迎大家来到贝蒂大讲堂✨✨ #x1f388;#x1f388;养成好习惯#xff0c;先赞后看哦~#x1f388;#x1f388; 所属专栏#xff1a;Linux学习 贝蒂的主页#xff1a;Betty’s blog 1. C语言文件操作
C语言文件操作接口如下#xff0c;详情可参照——C语言文… ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 养成好习惯先赞后看哦~ 所属专栏Linux学习 贝蒂的主页Betty’s blog 1. C语言文件操作
C语言文件操作接口如下详情可参照——C语言文件
文件操作函数功能fopen打开文件fclose关闭文件fputc写入一个字符fgetc读取一个字符fputs写入一个字符串fgets读取一个字符串fprintf格式化写入数据fscanf格式化读取数据fwrite向二进制文件写入数据fread从二进制文件读取数据fseek设置文件指针的位置ftell计算当前文件指针相对于起始位置的偏移量rewind设置文件指针到文件的起始位置ferror判断文件操作过程中是否发生错误feof判断文件指针是否读取到文件末尾
读写方式如下
文件使用方式含义如果指定文件不存在“r”只读为了输入数据打开一个已经存在的文本文件出错“w”只写为了输出数据打开一个文本文件建立一个新的文件“a”追加向文本文件尾添加数据出错“rb”只读为了输入数据打开一个二进制文件出错“wb”只写为了输出数据打开一个二进制文件建立一个新的文件“ab”追加向一个二进制文件尾添加数据出错“r”读写为了读和写打开一个文本文件出错“w”读写为了读和写建议一个新的文件建立一个新的文件“a”读写打开一个文件在文件尾进行读写建立一个新的文件“rb”读写为了读和写打开一个二进制文件出错“wb”读写为了读和写新建一个新的二进制文件建立一个新的文件“ab”读写打开一个二进制文件在文件尾进行读和写建立一个新的文件
下面是一个使用C语言文件的示例
#includestdio.h
int main()
{FILE*fpfopen(log.txt,w);if(fpNULL){perror(fopen fail:);return 1;}//open successconst char*msghello betty!\n;int count5;while(count--){fputs(msg,fp);}fclose(fp);return 0;
}一般而言如果没有定义对应的log.txt文件系统会在当前路径自动创建该文件。并且当前路径并不是指可执行程序所处的路径而是指该可执行程序运行成为进程时我们所处的路径。比如我们可以在上级目录执行testfile文件 可以看见log.txt是在该对应路径创建的而不是对应可执行文件所在目录创建的。
其中我们也可以通过监视进程的方式观察一下 然后我们可以看见两个软连接cwd和exe分别对应的就是进程运行时我们所处的路径以及可执行文件所处路径。
2. 三个默认打开流
我们常说Linux下一切皆文件那么我们的键盘与显示器自然也是文件。我们向键盘输入数据本质就是操作系统向键盘文件中读取数据我们能从显示器看见数据本质就是操作系统向显示器文件写入数据。但是我们在使用键盘与显示器时并没有手动进行任何文件相关的读写操作那我们又是如何对键盘文件与显示器文件进行读写的呢
答案自然是操作系统自动帮我们打开的任何进程在运行时操作系统都会默认打开三个输入输出流分别为标准输入流标准输出流以及标准错误流。对于C语言分别就是stdin、stdout以及stderr。对于C分别就是cin、cout和cerr自然其他语言也会有相似的概念因为这是操作系统所支持的而不是某个语言所独有的。
我们可以在Linux中的man查看对应的声明 其中标准输入流对应的就是我们的键盘而标准输出流与标准错误流对应的就是我们显示器。
其中我们也可以通过fputs函数验证一下
#includestdio.h
int main()
{//向显示器打印fputs(hello betty!\n,stdout);fputs(hello betty!\n,stdout);fputs(hello betty!\n,stdout);fputs(hello betty!\n,stdout);return 0;
} 3. 系统文件I/O
在前面我们学习操作系统时知道为了方便用户使用一般我们会对系统接口进行封装。我们的文件操作也不例外像fopenfclose等接口本质其实对操作系统提供的文件接口的封装。接下来我们就来学习一下系统提供的文件接口。
3.1 open函数
首先我们来介绍文件打开操作的系统接口。 pathname表示打开或者创建的目标文件若pathname以路径的方式给出则当需要创建该文件时就在pathname路径下进行创建。若pathname以文件名的方式给出则当需要创建该文件时默认在当前路径下进行创建。·flags表示打开文件的方式。mode表示创建文件的默认权限(八进制数)。 其中常用文件打开方式有如下几个
参数选项含义O_RDONLY以只读的方式打开文件O_WRNOLY以只写的方式打开文件O_APPEND以追加的方式打开文件O_RDWR以读写的方式打开文件O_CREAT当目标文件不存在时创建文件
如果想同时兼具多个打开方式可以使用逻辑与|链接两个选项。比如说我们想打开文件并且文件不存在时创建文件可以写成
O_WRNOLY|O_CREAT这些选项本质也就是一个宏定义其中flags是一个整型若将一个比特位作为一个标志位则理论上flags可以传递32种不同的标志位。 所以我们也可以使用按位与操作来检测是否设置某个选项
if (flagsO_RDONLY){//设置了O_RDONLY选项
}
if (flagsO_WRONLY){//设置了O_WRONLY选项
}
if (flagsO_RDWR){//设置了O_RDWR选项
}
if (flagsO_CREAT){//设置了O_CREAT选项
}
//...并且如果我们打开的文件已存在就使用第一个接口(两个参数)如果打开的文件不存在就需要使用第二个接口(三个参数)即需要为创建的文件设置默认权限。
如果我们要为文件设置默认权限就需要考虑文件默认掩码umask的影响。我们之前讲过文件的默认权限为mode(~mask)我们除了可以在命令行通过指令umask 八进制数来修改默认的掩码umask(默认为002)外还能在程序中调用umask函数进行修改。比如我们将umask设置为0
umask(0); //将文件默认掩码设置为0最后再来探究一下open的返回值也就是文件描述符fd。
#includestdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int main()
{umask(0);//设置文件掩码为0int fd1 open(log1.txt, O_RDONLY | O_CREAT, 0666);int fd2 open(log2.txt, O_RDONLY | O_CREAT, 0666);int fd3 open(log3.txt, O_RDONLY | O_CREAT, 0666);int fd4 open(log4.txt, O_RDONLY | O_CREAT, 0666);int fd5 open(log5.txt, O_RDONLY | O_CREAT, 0666);printf(fd1:%d\n, fd1);printf(fd2:%d\n, fd2);printf(fd3:%d\n, fd3);printf(fd4:%d\n, fd4);printf(fd5:%d\n, fd5);return 0;
}运行之后我观察到文件描述符是从3开始的并且依次递增这起始并不是偶然。至于为什么我们等会儿在揭晓。
当然这只是文件成功返回的情况如果文件打开失败那将返回-1。
3.2 close函数
我们可以调用系统接口close来关闭指定文件其原型为 int close(int fd); 使用close函数时传入需要关闭文件的文件描述符即可若关闭文件成功则返回0若关闭文件失败则返回-1。
3.3 write函数
同样我们也能通过系统接口write对文件进行写入其原型为 ssize_t write(int fd, const void *buf, size_t count); 其中fd指的是文件描述符buf为用户缓冲区而count为期望写的字节数。如果写入成功返回实际写入的字节数若写入失败则返回-1。
注意ssize_t其实就是一个有符号整型具体来说就是被typedef重新定义过typedef int ssize_t
以下我们可以利用write函数对一个log.txt文件进行写入
#includestdio.h
#includeunistd.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includestring.h
int main()
{int fdopen(log.txt,O_WRONLY|O_CREAT);if(fd0){//open errorperror(open fail:);return 1;}const char*msghello betty!\n;for(int i0;i8;i){write(fd,msg,strlen(msg));}close(fd);return 0;
}3.4 read函数
同样我们也能通过系统接口read对文件进行读写其原型为 ssize_t read(int fd, void *buf, size_t count); 其中fd指的是文件描述符buf为用户缓冲区而count为期望读的字节数。如果读出成功返回实际读出的字节数若读出失败则返回-1。
以下我们可以利用read函数对一个log.txt文件进行读出
#includestdio.h
#includeunistd.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includestring.h
int main()
{int fdopen(log.txt,O_RDONLY);if(fd0){perror(open fail:);return 1;}char buf[1024]{\0};ssize_t retread(fd,buf,1023);if(ret0)printf(%s,buf);close(fd);return 0;
}4. 文件描述符——fd
在我们的操作系统中文件是由我们进程所打开的存在大量进程就意味着存在大量被打开的文件。为了方便我们对文件进行管理我们就将每个文件struct file链入我们的双向链表之中。
struct File
{//包含了打开文件的相关属性//链接属性
};而一个文件也可能被多个进程所读写为了让操作系统能够准确识别每个进程对应的文件我们就一定要让进程与我们的文件建立联系。事实也是如此我们的进程控制块task_struct中就存在一个指针指向一个名为struct file_struct的结构体这个结构体中存在一个结构体指针数组struct file*fd_array[]分别存放着着每个文件struct file的地址。这样我们的进程就与文件建立起了联系。 一般我们的指针数组struct file*fd_array[]的012下标分别对应我们的标准输入流标准输出流标准错误流这三个文件而这些下标就是我们所说的文件描述符——fd。这也解释了我们打开文件的描述符为什么从3开始并且依次递增。并且通过对应的文件描述符进程只需要找到对应的指针数组fd_array就能访问对应的文件这也是为什么我们文件的系统调用接口的参数一定会有fd的原因。
当然如果我们在中途关掉某个文件操作系统就会为该下标重新分配对应的文件。
#include stdio.h
#include sys/stat.h
#include sys/types.h
#include fcntl.h
int main()
{close(0);close(2);int fd1 open(log1.txt, O_RDONLY | O_CREAT, 0666);int fd2 open(log2.txt, O_RDONLY | O_CREAT, 0666);int fd3 open(log3.txt, O_RDONLY | O_CREAT, 0666);int fd4 open(log4.txt, O_RDONLY | O_CREAT, 0666);int fd5 open(log5.txt, O_RDONLY | O_CREAT, 0666);printf(fd1:%d\n, fd1);printf(fd2:%d\n, fd2);printf(fd3:%d\n, fd3);printf(fd4:%d\n, fd4);printf(fd5:%d\n, fd5);return 0;
}我们也知道当一个程序运行起来时操作系统会将该程序的代码和数据加载到内存然后为其创建对应的task_struct、mm_struct、页表等相关的数据结构并通过页表建立虚拟内存和物理内存之间的映射关系。如果与我们的文件管理联系起来就是一个磁盘文件log.txt加载进内存形成内存文件最后加入对应双向链表中管理起来。 当文件存储在磁盘上时我们称之为磁盘文件。而当磁盘文件被加载到内存中后就变成了内存文件。磁盘文件与内存文件的关系恰似程序和进程的关系。程序在运行起来后成为进程同样磁盘文件在加载到内存后成为内存文件。磁盘文件主要由两部分构成即文件内容和文件属性。文件内容指的是文件中存储的数据而文件属性则是文件的一些基本信息包括文件名、文件大小以及文件创建时间等。这些文件属性也被称为元信息。在文件加载到内存的过程中一般会先加载文件的属性信息。这是因为在很多情况下我们可能只需要了解文件的基本属性而不一定立即需要对文件内容进行操作。当确实需要对文件内容进行读取、输入或输出等操作时才会延后式地加载文件数据。这样的设计可以提高系统的效率避免在不必要的时候浪费资源加载大量的文件数据。