文件操作:一切皆文件

文件类型

        Linux系统下所有文件均属于以下七种类型

                1)b,块设备文件(存储类设备,例如硬盘)

                2)c,字符设备文件(包括但不限于输入输出设备,例如鼠标键盘显示器)

                3)d,目录文件(文件夹)

                4)-,普通文件(包括但不限于xxx.c,xxx.h,xxx.txt,xxx.jpg,a.out)

                        文本文件:1.txt,xxx.c

                        二进制文件:xx.jpg,a.out

                        可以打开该文件来区分,若是乱码则是二进制文件

                5)l,软链接文件(快捷方式)

                6)s,套接字文件(用于网络通信)

                7)p,管道文件(用于进程间通信)

        例:用ls -l密令可以把当前文件的详细信息显示出来,作用等价于ll密令,以第一个文件为例

        第一个字符‘d’,表示该文件属于什么文件类型,为目录文件。

        三个rwx为文件操作权限,其中r(read)为读取权限,w(write)为写权限,x为可执行权限,-为无权限。第一组rwx为自己对文件的读写执行权限,第二组r-x为同组用户对文件的读写权限,剩下的是其他人对文件的读写权限。

        数字37为链接个数(37包括了本文件.和上一文件的..)

        两个linux分别表示用户名和组名,第一个进入组的成员名就是组名

        数字4096为文件大小

        日期为文件最后被修改的时间

        最后为文件名称(.为本文件,..为上一文件)

文件操作思想

        打开文件---->读写文件---->关闭文件

        在Linux操作系统中,默认打开三个文件

                FILE* stdin:标准输入流,对应于标准输入设备:键盘

                FILE* stdout:标准输出流,对应于标准输出设备:显示屏

                FILE* stderr:标准出错流,对应于标准出错设备:显示屏

文件操作方法

        标准IO和文件IO,其中I表示input,O表示output,即输入输出,所以IO表示的是对文件的操作

标准IO

        C库提供的一套对文件操作的操作方法,即库函数,位于man手册第三章节(第一章节和第二章节分别为Linux密令和系统调用)

        标准IO属于C库函数调用系统调用,移植性强(在win和linux下都能用),是系统调用的一次封装,增加了全缓冲区,减少了操作硬件的次数,进而提高了数据读写效率,主要用在对配普通文件的操作上,且操作方法比较多

        打开文件:fopen

        读/写文件:fgets/fputs(一个字符),fgets/fputs(一行数据),fread/fwrite(固定大小的二进制数据,例如结构体)

        关闭文件:fclose

        返回值为文件流指针

文件IO

        Linux内核专门为应用层提供的一套对文件操作的操作方式,即系统调用。必须基于Linux系统上,Windows操作系统上没有。

        属于系统调用,只能用于Linux操作系统,移植性弱,无缓冲区,主要针对于实时性较高的硬件操作上,也可以操作普通文件

        打开文件:open

        读/写文件:read/write

        关闭文件:close

        返回值为文件描述符,遵循最小未被分配原则

缓冲区

        1,行缓冲1k(1024字节),用于人机交互界面、终端

                缓冲区刷新方法:     

                        1)程序结束自动刷新

                        2)遇到\n刷新

                        3)fflush()函数强制刷新

                        4)缓冲区满自动刷新

                值得注意的是,printf函数的输出模式正是行缓冲,所以在输出时应在后面加上\n避免无法输出

        2,全缓冲4k(4096字节),用于文件缓冲区

                缓冲区刷新方法:

                        1)程序结束自动刷新

                        2)fflush()函数强制刷新

                        3)文件关闭刷新

                        4)缓冲区满自动刷新         

        3,无缓冲0k,用于出错信息对应设备

                stderr为无缓冲文件流

标准IO操作函数

fopen

        功能:打开一个文件,这个文件的名字为被pathname指向的字符串,并返回一个文件流指针

                文件流:数据从文件流入流出体现出来的字节流,用FILE表示,而FILE *为文件流指针,对应到了一个已打开的文件

        形参:

                pathname:打开的文件名地址

                mode:打开的方式,打开方式如下

        返回值:成功则会返回文件流指针FILE,失败则返回NULL

        注:虽然fopen是打开文件,但是并不会跳出一个图形化界面,只是说明接下来你可以通过这个文件流指针对该文件内容进行部分操作,但并不是所有文件都可以打开,例如目录,管道文件,或者被其他程序占用的文件就打不开

fclose

        功能:关闭一个流

        参数:文件流指针

fgetc、fputc

        一个字节一个字节读写;可用于文本文件和二进制文件;fgetc读完标志:返回EOF

fputc

        功能:向文件中只写入一个字符

        参数:要写入的字符c,要写入的文件流指针

        返回值:成功则为字符的ASCII码值,失败则为EOF(-1)这个宏

        在1.txt文件中清空并输入一个A,可以用od -c加文件名的形式,以字符的形式输出到终端

        此时1.txt中无内容,输出结果为-1

        如果是汉字的话,由于Linux默认采用UTF-8 编码,od -c会将字符拆成字节序列(以八进制形式)

向文件中输入“hello world”

        最前面的数字是八进制表示的字节位置,正好对应11个

fgetc

        功能:从文件中读取一个字符,fgetc(stdin)等价于getchar

        参数:要读取文件的文件流指针

        返回值:成功则为读到的字符的ASCII码,失败或读到文件末尾都是EOF

        该函数会自动向后读取,不是每次都从文件第一个字符开始读取

        在测试时出现了这样一种情况,如果1.txt为刚创建还未写入数据,或者是通过w来清空,此时什么都不会输出,然而如果输入数据再手动全部删除,那就会输出一个空行,这是由于在文本文件中只删除了可见字符,而换行符仍有残留

fgetc和fputc实现文件拷贝

主函数传参

        主函数int main(int argc, const char *argv[]),其中argc为程序运行时,给主函数传参的个数,argv指针数组,指向主函数传递的参数,argv[0]指向a.out。

        实参:运行程序时,./a.out aaa bbb,均为实参

        形参:主函数中的参数

        所以文件拷贝还可以通过以下方式

        不仅仅是文本文件,图片文件也可以拷贝

fgets、fputs

        一行一行读写;只能读写文本文件;fgets读完文件标志:返回NULL;fputs从终端读取数据时候会保留\n

fputs

       功能:向文件中写入字符串(不会写入'\0'字符)

       参数:要写入的字符串首地址,文件流指针

        返回值:成功则为非负的整型数据,失败则为EOF

fgets

        功能:从文件中最多读取一行数据,换行则停止

        参数:存放读取到数据的内存空间的首地址;希望读到的字节数;文件流指针

        返回值:成功则为s的首地址,失败或读到文件末尾则为NULL

        停止读取:读到\n;读到末尾;读完希望的字节数-1个字符(因为存放字符串最后需要有\0)

        如图所示,fgets并没有读取换行符后的内容

        由于od -c一行只显示16个字节,所以d在第二行输出


        若是想要读取一整个文件的内容,可以参考fgetc的循环,终止条件是返回值为NULL

        两个函数依然有向后读取的功能,如果同时使用则会继承文件流的位置,如下代码

        该代码并不会输出hello world,这是由于fputs函数已经将文件流的位置移动到了文件最后,而fgets会接着文件流的位置直接返回EOF

fgets和gets的区别
fgets gets
从指定的已打开文件中读取最多一行数据(遇到\n停止读取)(包括\n) 指定从终端读设备读取数据

保留\n字符并在字符串末尾添加\0

将终端读到的\n替换成\0
最多读取sizeof - 1个字符,最后一个位置存放\0 危险,在读取时没有大小限制,可能造成数据越界

        由于gets可能造成数组越界,所以gets函数几乎是被禁止使用的,而fgets会保留\n字符,所以在打印字符串会多打出一个换行,要解决这个问题只需要手动的将数组最后一个元素换成\0即可

fgets和fputs实现文件拷贝

        由于fgets最多读取一行数据,所以需要用到循环,直到读到文件末尾,但是该函数无法完全做到复制图片,会造成数据丢失,因为图片属于二进制文件,包含大量字节值为 0(\0)的数据,而fgets在读到\0时会停止,造成损失

        在文本文件中,所有数据都以字符的ASCII码形式存储,所以0可理解为'0',而在二进制文件中,数据以内存中的原始二进制形式存储,0可理解为'\0'

fread、fwrite

        读写固定大小数据;主要读二进制文件,也可读文本文件;fread读完文件标志:返回值为0

fwrite

        功能:向文件中写入nmemb个大小是size的数据到文件中

        参数:要写入的数据的首地址;每个元素的字节数;写入元素个数;要写入的文件流指针

        返回值:成功则返回实际写入的元素个数,失败则为小于nememb的数

fread

        功能:从文件中读取nmemb个大小是size的元素

        参数:存储读取到数据的内存的首地址;每个元素大小;读取元素个数;文件流指针

        返回值:成功则为实际读到的元素个数,读到文件末尾则为0

        如果读写个数超过数组的大小,则会造成数组越界访问效果,即读写出入随机值或者之间崩溃

fseek

        功能:实现文件流重新定位

        参数:需要定位的文件流指针;向后偏移量(负数则向前偏移);定位的相对位置(whence)

        返回值:成功则为0,失败则为-1

        从h开始,向后偏移10个,到d的位置,输入A,然后指针自动向后移,指向A之后,输入D,文件末尾从D之后开始,向前偏移5个,到o的位置,输入B,然后指向B之后的r,输入C

        在测试过程中,如果是从头向前偏移则会失败从而返回-1,但是从尾向后偏移却是成功的,如果此时输入数据,在末尾与偏移量之间会填充\0字符,如下

ftell

        功能:获取流的当前位置到文件开头的偏移量

        参数:文件流

        返回:从头向后的偏移量字节

模拟迅雷

        在第三步中,虽然可以写成fwrite(fpsrc, len, 1, fpdst)且不报错,但是这是U盘对U盘,文件的拷贝需要通过内存空间,即将文件内容放入内存空间中,在写在另一个U盘中

        为了避免读取数据时访问到不该访问的区域,可以通过循环,一次一个字节,分多次访问,可以参考fgetc

rewind

        功能:流复位函数,复位到开头,等价于fseek(fp, 0, SEEK_SET)

        参数:文件流指针

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐