C语言:文件操作详解

                                                     创作不易,友友们给个三连吧!!

一、为什么我们需要使用文件

       我们在写程序的时候,输入的数据是存储在电脑内存中的,如果程序退出内存回收,相应数据也就丢失了,等再次运行程序,就看不到上次输入的数据了,所以为了能够让我们的数据永久化保存,我们可以使用文件!

二、什么是文件

2.1 文件的分类

     在磁盘(硬盘上)的文件就叫做文件,在程序设计中,文件一般分为两种:程序文件、数据文件。

      程序文件包括源文件(.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)

      数据文件,因为文件的内容不一定是程序,也可能是程序运行时读写的数据,所以可以理解为程序运行需要从中读取数据的文件,或者输出内容的文件。

      本文内容以数据文件为主!!

 2.2 终端

      终端:包括显示器、鼠标、键盘、耳机、麦克风、显示器和摄像头等等。 这些外围设备就被称为终端,负责向主机输入数据的就叫输入终端,比如鼠标、键盘、麦克风、摄像头,负责接收主机输出数据的设备就被称作输出终端,比如显示器、耳机。

       以往在C语言程序编写中,我们处理数据的输入和输出都是以终端为对象的,即通过终端的键盘输入数据,并将运行结果显示在显示器上。

       但是这样的方式显然不能达到永久保存数据的目的,所以我们需要把信息输出到磁盘的文件中,当我们需要的时候再通过磁盘中的文件将数据读取到内存中去使用!

2.3 文件名

      我们为了将程序中输入的数据永久化保存起来,就需要用到文件,而每个文件都要自己的一个唯一的标识,用来方便用户的识别和引用。我们把这种文件标识叫做文件名。

     文件名包括了3部分:文件路径+文件名主干+文件后缀

     比如c:\code\test.txt 

     c:\code是文件路径,test是文件主干,.txt是文件后缀

一般想要知道某个文件的路径,即找到这个文件,右击属性,就可以找到他的位置

三、二进制文件和文本文件

      根据数据的组织形式,数据文件又被区分为文本文件二进制文件。

      数据在内存中是以二进制形式存储的,所以如果不加以转换输出到磁盘文件中,那该文件就是二进制文件。如果要求以ASCII码的形式是存储在磁盘文件中,那么在存储前需要将二进制转换成ASCII形势存储,那么该文件就被成为文本文件。

     数据在内存中的存储形式:字符一律以ASCII码形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

      .txt后缀的文件一般为文本文件的格式,是我们肉眼可以看得懂的,而二进制文件是我们肉眼看不懂得,但是编译器可以读懂。

四、文件的打开和关闭

      如何操作文件呢?操作文件的步骤其实和我们喝饮料一样,喝饮料的时候,先打开瓶盖,然后喝,最后关紧瓶盖。而我们想要操作文件,首先是打开文件,然后对文件进行读写操作,最后是关闭文件。

4.1 流

       我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了方便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。

     也就是说,流其实就是程序底层设计出来的一种东西,他存在于内存中的某一块区域,专门负责对接不同的外部设备,所以我们程序员并不需要关心我们的数据如何和外部设备建立联系,我们只需要关注流!!所以C程序针对⽂件、画面、键盘等的数据输⼊输出操作都是通过流操作的。

     ⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

 4.2 标准流

     在C程序中,我们通过键盘输入数据,并向屏幕中输出数据,其实都是通过流来完成。

因为在C语言程序启动的时候,默认打开了3个流:

• stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

• stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。

• stderr - 标准错误流,大多数环境中输出到显示器界流。perror函数就是将错误的信息输出到标准输出流中

      有了这三个流,我们就可以通过scanf/printf/perror函数来进行输入和输出操作。

      而流本身作为程序底层开发出来的内容,存在于内存中的某一块区域,C语言中,通过FILE*的文件指针来维护流的各种操作。

      stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为⽂件指针。

4.3 文件指针

     缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。

     每一个被使用的文件都在内存中开辟了一个相应的文件区,专门用来存放文件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系 统声明的,取名FILE。

     例如,VS2013编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型申明:

struct _iobuf {char *_ptr;int _cnt;char *_base;int _flag;int _file;int _charbuf;int _bufsiz;char *_tmpfname;};
typedef struct _iobuf FILE;

    不同的C编译器的FLIE类型包含的内容不完全相同,但是大同小异,理论上来说,越高级的编译器,封装得会越完善,所以越不容易观察到这些细节。

    也就是说,我们每当打开一个文件,系统就会根据文件的情况况⾃动创建⼀个FILE结构的变量,并填充其中的信息,使⽤者不必关⼼细节。

    ⼀般都是通过⼀个FILE*的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。

   如图,首先我们打开文件,其实就是向内存中申请了一块空间,该空间的类型是FILE类型,我们把它称为文本信息区,该文本信息区首先会根据原有的文本信息进行填充,之后再由操作者利用FILE*指针对这个文本信息区进行读或者写的操作,我们程序员不需要知道文件(data.txt)是如何和这个用结构体变量存储的文件信息区建立联系的,我们只需要知道我们可以用FILE*来操作文件,该文本信息区就相当于"流",我们通过"流"和外部设备建立联系。

   4.4  文件的打开和关闭

       文件在读写之前要打开文件,使用结束后应关闭文件。

       而我们在打开文件的同时,都会返回一个FILE*类型的指针,来帮助我们建立和该文件的联系,我们可以通过这个指针来进行读写的操作。

      ANSIC 规定使⽤ fopen 函数来打开文件, fclose 来关闭文件。

4.4.1 fopen

const char * filename:传需要打开的文件的文件名
const char * mode :传该文件的打开方式
Open file:作用是打开文件

返回类型:FILE*指针,我们可以通过该指针完成对文件的读写操作(相当于通过该指针打开了一个文件流)

注:文件可能会打开失败,如果FILE*指针成功返回,说明打开成功,如果返回的是NULL,则打开失败!

4.4.2 fclose

FILE * stream:传维护该文件流的指针
Close file:作用是关闭文件

4.4.3 打开模式

   fopen函数中的参数mode表示文件的打开模式,下面都是文件的打开模式。

4.4.3.1 w和a的区别

    w和a都可以写,但是如果我们打开的文件原来保存着一些数据,用w的话,会先清空掉文件的数据再进行写的操作,而用a则是直接在这些数据的后面进行追加

4.4.3.2 r+、w+、a+的区别

   r+是覆盖写,即在原来文件数据的基础上从头开始覆盖写入

   w+是清空写,即原来文件数据会被清空再开始写

   a+是追加写,即在原本文件数据的基础上在后面追加写入

4.4.4 文件的存储路径

1.相对路径

int main()
{FILE* pf;//打开文件pf = fopen("./../data.txt", "w");//桌面生成if (pf == NULL)//如果打开失败{perror("fopen");return 1;}//文件操作//关闭文件fclose(pf);return 0;
}

./   当前目录
../  上一级目录
/    根目录

2.绝对路径

int main()
{FILE* pf;//打开文件pf = fopen("C:\\Users\\lenovo\\Desktop\\data.txt", "w");//桌面生成if (pf == NULL)//如果打开失败{perror("fopen");return 1;}//文件操作//关闭文件fclose(pf);return 0;
}

五、文件的顺序读写

顺序读写函数介绍:

     上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀ 般指适⽤于标准输出流和其他输出流(如⽂件输出流)。

5.1 fputc

int character :传需要输出的字符
FILE * stream:传需要输出的流
Write character to stream:作用是将字符写入流中

      放置的过程其实跟光标有关,最开始光标是在最前面,输出a的时候光标移到b的后面了,所以再输出的b就是在a的后面,以此类推。

     fpuc可适用于所有输出流,所以也可以使用标准输出流

5.2 fgetc

FILE * stream:传需要读取的流
Get character from stream:作用是从流中获取字符

注:如果读取失败,会返回EOF;

原本文件里有abcd,我们调用了四次fgetc将他读取出来并打印在屏幕上。

与fputc一样,fgetc也适用于标准输入流,所以也可以从键盘读取

但是因为读取也是按照光标去逐个读取的,所以如果加上空格,就会有的数据读取不到

写一个代码,完成将data.txt文件的内容,拷贝一份生成data1.txt文件

int main()
{//打开文件FILE* pfread = fopen("data.txt", "r");if (pfread == NULL)//如果打开失败{perror("fopen->data.txt");return 1;}FILE* pfwrite = fopen("data1.txt", "w");if (pfwrite == NULL)//如果打开失败{//第二次打开文件,如果打开失败,返回前必须把前一个文件关闭掉并置空fclose(pfread);pfread = NULL;perror("open->data1.txt");return 1;}//文件操作//数据的拷贝int ch = 0;while ((ch = fgetc(pfread)) != EOF)//循环读取直到读取不到{fputc(ch, pfwrite);}
//循环结束后可以做到完全拷贝//关闭文件fclose(pfread);fclose(pfwrite);return 0;
}

5.3 fputs

 const char * str:传需要输出的字符串FILE * stream:传需要输出的流
Write string to stream:作用是将字符串写入流中

5.4 fgets

char * str:传字符串存放的地址(一般是一个字符数组)
int num:传从流中读取的最大字符串长度(由于字符串末尾有一个\0,所以默认其实最多读取num-1个)
FILE * stream:传需要读取的流
Get string from stream:作用是从流中获取字符串

我们可以看到,由于字符串的末尾要默认跟一个\0,所以最多只能读取num-1个到arr数组中

5.5 fprintf

FILE * stream:传需要输出的流
const char * format:传格式化字符串,用于指定输出的格式。
...:可变参数列表
Write formatted data to stream:作用是将一块格式化的数据输出到流中

5.6 fscanf

FILE * stream:传需要读取的流
const char * format:传格式化字符串,用于指定输入的格式。
...:可变参数列表
Read formatted data from stream:作用是从流中读取一块格式化的数据

注:%f打印时可以控制格式

5.7 fwrite

const void * ptr:传要写入的数据的指针
size_t size:传每个数据的大小
size_t count:传要写入数据项的数量
FILE * stream:传需要输出的流
Write block of data to stream:作用是将整块的数据输出到流中

    因为是二进制形式,所以我们看不懂,但是编译器可以看懂,所以要使用fwrite就要配套使用fread!!

注:无论是什么文件,字符都是以ascii码值存储的,所以打印出来都一样!!

5.8 fread

const void * ptr:传要写入的数据的指针
size_t size:传每个数据的大小
size_t count:传要写入数据项的数量
FILE * stream:传需要输入的流
Read block of data to stream:作用是将整块的数据输入到流中

将fwrite输出到文件里的数据通过fread成功读取回来。

六、一些函数的对比

6.1 scanf/fscanf/sscanf

const char * s:用于读取的字符串
const char * format:格式化的字符串,用于输入指定的格式...:可变参数列表
Read formatted data from string:在字符串中读取一个格式化的数据

       对比一下参数,共同点都是读取一个格式化的数据,不同的是scanf是默认的标准输入流,从键盘上读取,而fscanf是所有的标准输入流都可以,参数可以传文件流也可以跟scanf一样传stdin(标准输入流),而sscanf是从一个字符串中读取。

6.2 printf/fprintf/sprintf

const char * s:用于输出的字符串
const char * format:格式化的字符串,用于输出指定的格式
...:可变参数列表
Write formatted data from string:把一个格式化的数据转换成字符串

      对比一下参数,共同点都是输出一个格式化的数据,不同的是printf是默认的标准输出流,输出到屏幕上,而fprintf是所有的标准输出流都可以,参数可以传文件流也可以跟printf一样传stdout(标准输入流),而sprintf是输出到一个字符串中。

6.3 总结

scanf:针对标准输入(键盘)的格式化输入函数

printf:针对标准输出(屏幕)的格式化输出函数

fscanf:针对所有输入流的格式化输入函数

fprintf:针对所有输出流的格式化输入函数

sscanf:从一个字符串中读取格式化的数据

sprintf:把一个格式化的数据转换成字符串

七、文件的随机读写

      顺序读写,顾名思义就是按顺序读写,本质是根据光标的移动按顺序读写,而光标最开始就是在文件的最前面。

     这里的随机,并不是类型随机数的那种随机,而是我们想在文件哪个位置读取就在哪个位置读取,想在文件哪个位置输出就在哪个位置输出,那么我们想要实现文件的随机读写,就要想办法去操控光标

    而由于流是通过FILE*指针维护的,所以本质上,也是通过操控指针来达到操控光标的目的。

7.1 fseek

FILE * stream:需要操作的流
long int offset: 偏移量
int origin:起始位置

这个origin可以传3个参数:

SEEK_SET:起始位置

SEEK_CUR:当前光标的位置

SEEK_END:文件的末尾

Reposition stream position indicator:作用是重新定位光标位置指示器(根据⽂件指针的位置和偏移量来定位⽂件指针

7.2 ftell

 FILE * stream:需要操作的流
Get current position in stream:作用是获取流中的当前位置(返回⽂件指针相对于起始位置的偏移量

利用fseek和ftell来判断文件有多少个字节(fseek操控光标到尾部,再由ftell返回长度)

7.3 rewind

 FILE * stream:需要操作的流
Set position of stream to the beginning:作用是将流的位置设置为开头(即让文件指针返回到文件的起始位置)

八、文件读取结束的判定

8.1 feof

 FILE * stream:需要操作的流
Check end-of-file indicator:作用是检查文件结束指示符(即在文件读取结束后,判断是否是因为遇到文件末尾而结束)

8.2 ferror

 FILE * stream:需要操作的流
Check error indicator:作用是检查错误指示灯(即检查操作目标文件的过程中是否出现了错误)

8.3 判断文本文件是否读取结束的方法

⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

例如:

• fgetc 判断是否为 EOF

• fgets 判断返回值是否为 NULL .

8.4 判断二进制文件是否读取结束的方法

⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。

例如:

• fread判断返回值是否小于实际要读的个数。

8.5 判断文件是因为什么原因结束的

8.5.1 文本文件   

8.5.2 二进制文件

8.6 putchar

int character:要输出的字符
Write character to stdout:作用是将字符写到屏幕上

8.7 getchar

Get character from stdin:作用是从键盘获取字符

九、文件缓冲区

        ANSIC 标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为 程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。

 这⾥可以得出⼀个结论:

        因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件。 如果不做,可能导致读写⽂件的问题(数据丢失)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/662398.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VR全景技术如何运用在文旅展示,VR全景技术对景区有哪些好处

引言: 随着科技的不断进步和社会的不断发展,VR全景技术越来越受到人们的关注。在文化旅游行业中,VR全景技术的应用为景区提供了全新的展示方式和体验内容,极大地丰富了游客的文化旅游体验。那么VR全景技术能给文旅展示带来哪些好…

SpringBoot集成H2数据库

1&#xff09;添加H2的依赖 <dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>compile</scope> </dependency>2&#xff09;添加连接配置&#xff0c;启用web控制台 spring:datasource:url…

UE4 C++ UGameInstance实例化

1.创建GameInstance C类 2.在.h添加变量 class 工程名称_API UMyGameInstance : public UGameInstance {GENERATED_BODY()public: //定义了三个公开的变量UMyGameInstance();UPROPERTY(EditAnywhere, BlueprintReadWrite, Category "MyGameInstance")FString Name…

gitlab ci cd 不完全指南

gitlab 可能大家很常用&#xff0c;CI、CD 也应该早有耳闻&#xff0c;但是可能还没有去真正地了解过&#xff0c;这篇文章就是我对 gitlab CI、CD 的一些理解&#xff0c;以及踩过的一些坑&#xff0c;希望能帮助到大家。 什么是 CI、CD CI&#xff08;Continuous Integrati…

云计算底层技术、磁盘技术揭秘虚拟化管理、公有云概述

查看本机是否具备虚拟化支持 硬件辅助虚拟化 处理器里打开 虚拟化Inter VT-x/EPT 或AMD-V 构建虚拟化平台工具软件包 yum 与 dnf Yum和DNF都是用于管理Linux系统中的软件包的工具&#xff0c;但它们在许多方面存在一些差异。以下是一些可能的区别&#xff1a; 依赖解…

Python爬虫某云免费音乐——多线程批量下载

重点一&#xff1a;每首音乐的下载地址 重点二&#xff1a;如何判断是免费音乐 重点三&#xff1a;如何用线程下载并保存 重点四&#xff1a;如何规避运行错误导致子线程死掉 重点五&#xff1a;如何管理子线程合理运行 需要全部代码的私信或者VX:Kmwcx1109 运行效果&…

通过阿里云仓库来下载docker镜像

本文的前提是你已经在你的centos中安装了docker。 Step1&#xff1a; 注册登录阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台注册一个阿里云&#xff08;直接用支付宝扫码就行&#xff09;。 Step2&#xff1a; 创建个人实例 ​ Step3&#xff1a; 进…

关于bypassuac的探究——bypass的实现

经过前面的探究过后&#xff0c;我们整理下思路&#xff0c;首先要创建注册表&#xff0c;并添加DelegateExecute这个键值对&#xff0c;并修改command的指向exe路径即可bypassuac&#xff0c;那么这里用到一下几个函数 RegCreateKeyExA 首先是创建注册表项&#xff0c;对应的…

书客、米家、柏曼大路灯哪款好?多维度实测对比推荐!

每到寒暑假&#xff0c;各个论坛上出现“大路灯怎么选”的类似话题非常频繁&#xff0c;因为现在的孩子出来上学期间需要读写之外&#xff0c;在寒暑假时也在不断的学习&#xff0c;许多家长关注到孩子学习时的光线问题&#xff0c;担心影响到孩子的视力状况&#xff0c;都纷纷…

HTTP(Java web方向补充篇)

HTTP&#xff08;Java web方向补充篇&#xff09; HTTP简介 概念&#xff1a;Hyper Text Transfer Protocol,超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 HTTP协议特点&#xff1a; 基于TCP协议&#xff1a;面向连接&#xff0c;安全基于请求-响应模…

如何有效获取 Go 变量类型?探索多种方法

嗨&#xff0c;大家好&#xff01;本文是系列文章 Go 小技巧第九篇&#xff0c;系列文章查看&#xff1a;Go 语言小技巧。 文章目录 Go 的类型系统类型获取使用 fmt.Printf类型选择类型选择反射 reflect.TypeOf 其他注意点错误处理性能考量 总结 在 Python 中&#xff0c;可以使…

freertos 源码分析二 list链表源码

list.c 一、链表初始化 void vListInitialise( List_t * const pxList ) { pxList->pxIndex ( ListItem_t * ) &…

求n的k次方

递归法&#xff1a; #include<stdio.h> int mi(int n, int k) {if (k 1) //如果是1次方{return n; //返回n的1次方&#xff08;也就是n&#xff09;}else{return n * mi(n, k - 1); //n*n的k-1次方} } int main() {int n 0, k 0, sum 0;printf("请输入n值…

【Spark实践6】特征转换FeatureTransformers实践Scala版--补充算子

本节介绍了用于处理特征的算法&#xff0c;大致可以分为以下几组&#xff1a; 提取&#xff08;Extraction&#xff09;&#xff1a;从“原始”数据中提取特征。转换&#xff08;Transformation&#xff09;&#xff1a;缩放、转换或修改特征。选择&#xff08;Selection&…

Day02-数据类型和运算符(基本数据类型转换,赋值运算符,算术运算符,关系运算符,逻辑运算符,条件运算符,位运算符,赋值运算符,运算符优先级,标点符号)

文章目录 Java基础语法学习目标1 基本数据类型转换&#xff08;Conversion&#xff09;&#xff08;掌握&#xff09;1.1 自动类型转换&#xff08;隐式类型转换&#xff09;1.2 强制类型转换&#xff08;显式类型转换&#xff09;1.3 基本数据类型与字符串类型的转换 2 运算符…

FluxMQ:新一代的高性能MQTT代理服务器

FluxMQ&#xff1a;新一代的高性能MQTT代理服务器 前言 FLuxMQ是一款基于java开发&#xff0c;支持无限设备连接的云原生分布式物联网接入平台。FluxMQ基于Netty开发&#xff0c;底层采用Reactor3反应堆模型&#xff0c;具备低延迟&#xff0c;高吞吐量&#xff0c;千万、亿级…

二、Gradle 与 Idea 整合

这里写自定义目录标题 1、Groovy简介2、Groovy 安装3、创建 Groovy 项目4、Groovy 基本语法5、在 idea 中创建普通 java 工程 1、Groovy简介 详细了解请参考&#xff1a;http://www.groovy-lang.org/documentation.html 2、Groovy 安装 下载后解压到本地 验证&#xff1a; …

2024美赛数学建模A题思路源码——七鳃鳗性别比例和生态系统关系

赛题目的:分析一个物种根据资源可用性改变其性别比例的能力的利弊。开发一个模型,分析对生态系统中由此产生的相互作用。 问题一.七鳃鳗性别比例对生态系统的影响 问题分析 建立一个简化版的模型,来探讨以下问题: 1.我们假设七鳃鳗种群的增长遵循Logistic生长模型,其中食…

(学习日记)2024.02.01:引用变量 / 默认实参 / 一元作用域运算符 / 函数重载

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

【ChatGPT】文本向量化与余弦相似度:揭开文本处理的神秘面纱(5)

1、引言 在这个数字化的时代&#xff0c;我们每天都会面对大量的文本信息&#xff0c;从社交媒体到新闻报道&#xff0c;文本无处不在。但是&#xff0c;计算机要如何理解和处理这些文字呢&#xff1f;本文将为大家揭开其中的一些奥秘&#xff0c;详细解释文本向量化的概念&am…