首先我们需要知道几个问题:
1.为什么使⽤⽂件?
2.什么是⽂件?
3.⼆进制⽂件和⽂本⽂件?
4.⽂件的打开和关闭
5.⽂件的顺序读写
6.⽂件的随机读写
7.⽂件读取结束的判定
8.⽂件缓冲区
一: 为什么使用文件?
如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失 了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化的保存,我们可以使⽤ ⽂件。
二:什么是文件?
1.磁盘(硬盘)上的⽂件是⽂件。 但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件、数据⽂件(从⽂件功能的⻆度来分类 的)。
2.程序⽂件 程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows 环境后缀为.exe)。
3数据⽂件 ⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或 者输出内容的⽂件。 本章讨论的是数据⽂件。 在以前各章所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到 显⽰器上。 其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处 理的就是磁盘上⽂件。
4 ⽂件名 ⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。 ⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀 例如: c:\code\test.txt 为了⽅便起⻅,⽂件标识常被称为⽂件名。
三:⼆进制⽂件和⽂本⽂件
根据数据的组织形式,数据⽂件被分为⽂本⽂件和⼆进制⽂件。 数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂ 本⽂件。 ⼀个数据在⽂件中是怎么存储的呢? 字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽ ⼆进制形式输出,则在磁盘上只占4个字节。
四:⽂件的打开和关闭
-----流的概念
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出 操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流 想象成流淌着字符的河。 C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。 ⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。
-----标准流
在c语言中会默认打开一些流来供我们使用
stdin-标准输⼊流
stdout-标准输出流
stderr-标准错误流
当然他们的类型都是FILE*类型的流我们在后续的使用也会用到这个类型来创作一些流
首先介绍两个函数
1-》fopen--->用来打开文件的函数
FILE * fopen ( const char * filename, const char * mode );
filename->要打开文件的名字,以什么路径进行打开的
mode->以什么方式来打开的,比如读,写,追加。
返回值:
成功:FILE*类型的指针
失败:返回空
所以我们打开的时候要进行判空。
2-》fclose--->用来关闭文件的函数
注意:这关闭文件不会直接将FILE*类型的指针置为空我们要手动的置为空来进行后续的操作
我们在文件中一般分为三部分
1.打开文件 2.操作文件 3.关闭文件
这三部分来展开进行
int main() { //操作打开文件 FILE * ps=fopen("D:\\课件\\./../test1.txt","a");// /->调整上一级 if (ps != NULL) { printf("文件打开成功\n"); } else { perror("文件打开失败原因是:"); //文件打开失败原因是:: No such file or directory } //操作文件 //关闭文件 fclose(ps);//将ps给关闭用法和free一样 ps = NULL; if (ps == NULL) { printf("文件已关闭\n"); } return 0; }别忘了关闭文件的时候要手动置为空防止后续使用出现错误
当然代开文件有读写追加那么我们要用什么来进行呢
我们有这么多的方法进行添加要注意的是用’w‘进行写源文件的信息会丢失的所以源文件的信息要是有用的话就不要进行'w'可以选择追加或者”rb“进行操作
我们有了这些方法该如何去做呢接下来我们就要进行系统的说明了
五:⽂件的顺序读写
这里我们还要使用一些函数来进行这些操作
fputs:
int fputc ( int character, FILE * stream );
1.stream--->你打开文件所用的流
2.character--->你要输入的字符
返回值:
成功时返回写⼊的字符
失败时返回 EOF (通常是-1),错误指⽰器会被设置,可通过ferror来进行判断
演示 :写入A-Z
int main() { 开启文件 FILE* a = fopen("文件使用.txt", "w+"); if (a == EOF) { perror("a fail"); exit(1); } printf("文件打开成功\n"); //写文件 for (int i = 'A'; i <= 'Z'; i++) { fputc(i, a); } //关闭文件 fclose(a); a = NULL; if (a == NULL) { printf("文件关闭成功\n"); } return 0; }fgetc
int fgetc ( FILE * stream );
功能:从参数 stream 指向的流中读取⼀个字符。
函数返回的是⽂件指⽰器当前指向的字符,读取这 个字符之后,⽂件指⽰器⾃动前进道下⼀个字符。 参数: stream :FILE*类型的⽂件指针,可以是 stdin ,也可以是其他输⼊流的指针。
如果是 stdin 就 从标准输⼊流读取数据。
如果是⽂件流指针,就从⽂件读取数据。
返回值: • 成功时返回读取的字符(以 int 形式)。
• 若调⽤时流已处于⽂件末尾,函数返回 若发⽣读取错误,函数返回 EOF 并设置流的⽂件结束指⽰器( EOF 并设置流的错误指⽰器( ferror )。
打印刚才写入的A-Z的所有数
for (int i = 1; i <= 26; i++) { char c=fgetc(a); printf("%c", c); } printf("\n");feof
int feof ( FILE * stream );
检测 stream 指针指向的流是否遇到⽂件末尾(说人话就是你的文件的光标指向的位置)
ferror
int ferror ( FILE * stream );
fputs and gets
int fputs ( const char * str, FILE * stream );
str-->要写入的字符串
stream-->文件流
返回值:
成功--->非负整数
失败--->EOF
char * fgets ( char * str, int num, FILE * stream );
str-->读取到的字符数组
num-->读取的字符个数
stream-->流
返回首字符的地址/指针
这里提到的后续会讲到(文件从一开始计数)
int main() { //打开文件 FILE* pr = fopen("文件使用.txt", "w+"); if (pr == NULL) { perror("fopen fail!"); exit(1); } char arr[20] = { 0 }; //文件操作 fputs("hello word", pr); fflush(pr);//强制刷新缓冲区让写的数据先到后 rewind(pr);//让光标回到起始位置 fgets(arr, 6, pr); printf("%s\n", arr); //关闭文件 fclose(pr); pr == NULL; }输出结果
fprintf
它可以指定类型输出,前面介绍的只能输入字符串而这个可以传任何类型
int fprintf ( FILE * stream, const char * format, ... );
功能: fprintf 是将格式化数据写⼊指定⽂件流的函数。它与 printf 类似,但可以输出到任意 ⽂件(如磁盘⽂件、标准输出、标准错误等),⽽不仅限于控制台。
stream-->流
其他的和printf相同
返回值:
• 成功时,返回写⼊的字符总数(⾮负值)。
• 失败时,先设置对应流的错误指⽰器,再返回负值,可以通过 ferror() 来检测。
对应的代码和文件内部的情况
struct ass { char name[20]; char sex[10]; int age; char dizhi[20]; }s1; int main() { //打开文件 FILE* pr = fopen("文件使用.txt", "w"); if (pr == NULL) { perror("fopen fail!"); exit(1); } scanf("%s %s %d %s", s1.name,s1.sex,&s1.age,s1.dizhi); fprintf( pr,"姓名:%s,性别:%s,年龄:%d,地址: %s", s1.name, s1.sex, s1.age, s1.dizhi); //关闭文件 fclose(pr); pr == NULL; }fscanf
int fscanf ( FILE * stream, const char * format, ... );
stream-->流
其他的和scanf一样,他是读取一个数据覆盖到另一个
• 成功时,函数返回成功填充到参数列表中的项数。该值可能与预期项数⼀致,也可能因以下原因少 于预期(甚⾄为零):
fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
用来写2进制的可以是不同类型
ptr-->要写入的数据
size->数据的大小(以字节为单位)
count-->要写入的个数
stream-->流
fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
使用二进制可以重要的信息保存的更好这样可以对数据的更好的保护
别人打开二进制是乱码的样子
我们使用的时候想要看的时候将文件添加进去然后改成添加现有项打开方式改成二进制的形式就能看到里面的数据了.
sprintf
int sprintf ( char * str, const char * format, ... );
功能:将格式化数据写⼊字符数组(字符串)。它类似于 printf ,但输出⽬标不是控制台或⽂件, ⽽是⽤⼾指定的内存缓冲区。常⽤于动态⽣成字符串、拼接数据或转换数据格式。简⽽⾔之就是将格 式化的数据转换成⼀个字符串。
参数:
• str :指向字符数组的指针,⽤于存储⽣成的字符串(需确保⾜够⼤以防⽌溢出)。
• fo rmat :格式化字符串,定义输出格式(如 %d 、 %f 、 %s 等)。 • ... :可变参数列表,提供与格式字符串中说明符对应的数据。
返回值:
• 成功时:返回写⼊ buffer 的字符数(不包括结尾的空字符 \0 )。
• 失败时:返回负值
这里实现一下结果采用调试的结果来看一下
sscanf
int sscanf ( const char * str, const char * format, ...);
功能:从字符串中读取格式化数据。它与 scanf 类似,但输⼊源是内存中的字符串⽽⾮控制台或⽂ 件。常⽤于解析字符串中的结构化数据(如提取数字、分割⽂本等)。
参数:
• str :要解析的源字符串(输⼊数据来源)。
• format :格式化字符串,定义如何解析数据(如 %d 、 %f 、 %s 等)。
• ... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配)
返回值:
• 成功时:返回成功解析并赋值的参数数量(⾮负值)。
• 失败或未匹配任何数据:若输⼊结束或解析失败,返回EOF
这里采用调试的方法:
这里将我们所见到的
printf and fprintf and sprintf
scanf and fscanf and sscanf
六. ⽂件的随机读写
fseek
int fseek ( FILE * stream, long int offset, int origin );
stream-->流origin
origin-->这个很重要是要结合返回值的'
offset--->要结合前一个数让光标是正序,中序,后序的回几个
SEEK_SET是回到是首位置光标前进几个位置
SEEK_CUR是当前位置前进几个位置
SEEK_END回到末尾来前进几个位置这里origin一般选负数
这里都是在0的开始进行的比如给1-->0走一步--->1走一步是两部
int main() { FILE* pr = fopen("文件使用.txt","w+"); if (pr == NULL) { perror("文件打开失败!"); exit(1); } for (int i = 'A'; i < 'Z'; i++) { fputc(i, pr);//将A-Z的数写入文件 } //这样我们读文件的时候光标在最后一个 fflush(pr);//刷新一下将数据强制将缓冲区强制到文件 fseek(pr,3,SEEK_SET);//回到初始位置读取的我应该是D printf("%c\n", fgetc(pr)); fseek(pr, 1, SEEK_CUR);//从当前位置向后走一个应该是F printf("%c\n", fgetc(pr)); fseek(pr, -1, SEEK_END);//结尾走应该是Y printf("%c\n", fgetc(pr)); //关闭文件 fclose(pr); pr = NULL; return 0; }ftell
long int ftell ( FILE * stream );
计算当前位置距离初始位置返回值是距离
rewind
是将光标直接回到起始距离
void rewind ( FILE * stream );
七. ⽂件缓冲区
ANSI C标准采⽤“缓冲⽂件系统”处理数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程 序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲 区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊ 到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲 区的⼤⼩根据C编译系统决定的。
fflush
int fflush ( FILE * stream );
功能:强制刷新参数 stream 指定流的缓冲区,确保数据写⼊底层设备。
• 对输出流:将缓冲区中未写⼊的数据⽴即写⼊⽂件。
• 对输⼊流:⾏为由具体实现决定,⾮C语⾔标准⾏为(可能清空输⼊缓冲区)
• 参数为 NULL 时:刷新所有打开的输出流
参数: stream :指向⽂件流的指针(如 stdout 、⽂件指针等)
返回值:成功返回 0 ,失败返回 EOF
注意事项: 1. 仅对输出流或更新流(最后⼀次操作为输出)有明确刷新⾏为 2. 输⼊流的刷新⾏为不可移植(如清空输⼊缓冲区是⾮标准特性) 3. 程序正常终⽌( exit )或调⽤ fclose 时会⾃动刷新,但程序崩溃时缓冲区数据可能丢失