C语言文件操作零基础教程 C语言文件操作零基础新手入门保姆级教程
罅隙- 人气:0一、前言
我们如何使我们设计的程序具有“记忆功能”呢?答案是将数据以文件的形式另外保存。保存的形式有很多,在本文中我们以最简单的文本形式保存在记事本上,相信这篇文章一定让你学会。
二、文件操作基础知识
①什么是文件
我们一般谈的文件有两种:程序文件、数据文件
<程序文件>
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
<数据文件>
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
本章讨论的是数据文件
②数据文件类型
文本文件与二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是<二进制文件>(后缀.bin)。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是<文本文件>(后缀.txt)。
③数据如何存储
一个数据在内存中是怎么存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(按照一个字符一个字符储存,1 0 0 0 0每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(int)
④如何读取二进制文件
文本文件是我们通过记事本可以直接理解读取的,而二进制文件如果以记事本的形式打开是我们不能识别的乱码。但vs可以以某种方式读取二进制文件,方法如下:
(下图表示以记事本读取以二进制形式保存的10000)
演示二进制文件打开方式:
第一步:将文件添加到vs下
第二步:右击后打开方式选择二进制编译器
这就是最后的效果了。
⑤什么是文件名
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c: \code\ test.txt (在后文会提到对文件名的绝对引用和相对引用)
⑥文件缓冲区
含义:ANSIC 标准采用“缓冲文件系统”处理的数据文件。所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。通过磁盘缓存来实现,磁盘缓存本身并不是一种实际存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的存储空间, 来暂存从磁盘中读出(或写入)的信息。。
特点:从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的 。
[扩展-三种类型的缓存区] 链接-文件缓冲区
⑦文件指针
<文件指针>
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及
文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
FILE*p//文件类型指针
可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。
三、文件操作函数
①fopen 与 fclose
1.fopen
<功能>打开文件
<参数> filename-文件名 mode-打开方式
<返回值>一个文件指针
(打开方式mode表)
2.fclose
<功能> 关闭文件
<参数> stream-文件指针
3.使用示范
#include<stdio.h> #include<sting.h> #include<errno.h> int main() { FILE*pw = fopen("test.txt","w");//打开的文件为“test.txt” 打开方式为“w” if(pw==NULL)//若打开失败则说明失败原因并结束进程 { printf("%s",strerror(errno));//errno是全局错误变量 strerror将errno解析为错误原因 return 0; } fclose(pw);//关闭文件 pw = NULL;//将指针置为空,防止被误用 return 0; }
文件名相对/绝对路径
绝对路径:如c:\code\test.txt 包含文件路径 文件主干 文件后缀
相对路径:上图的表示就是相对路径,表示txt文件与源文件在同一路径下,若要表示上一路 劲,则用“../”,一次类推上上路径就是“../../”
FILE*pw = fopen("../test.txt","w");
②fputc与fgetc
1.fputc——输出函数
<功能>将一个字符写入流中
<参数>c-输出的字符
<返回值>正常——返回输出的字符
错误——返回EOF
<适用>所有流
2.fgetc——输入函数
<功能>从流中读取一个字符
<返回值>int——返回输入的字符
EOF——发生错误或到达文件结尾
<适用>所有流
3.使用示范
#include <stdio.h> #include <string.h> #include<errno.h> //fputc 输出一个字母 int main() { FILE*pw = fopen("test.txt","w"); if (pw == NULL) { printf("%s",strerror(errno)); return 0; } fputc('b',pw);//将字符‘b'写入文件"test.txt" fclose(pw); pw = NULL; return 0; } //fgetc 读取一个字符,读取一个后文件指针往后偏移一位 int main() { FILE*pr = fopen("test.txt","r"); if (pr == NULL) { printf("%s",strerror(errno)); return 0; } char ch = 0;//用ch接收输入的字符 ch = fgetc(pr);//从文件"test.txt"中读取,读取一个字符后文件指针自动往后移一位 fclose(pr); pr = NULL; printf("%c",ch); return 0; }
4.对所有流的理解
什么是流:流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存 向外部输出设备(显示器)输出的过程。
上述代码演示的是对文件流的操作,我们在用标准输入(stdin)输出(stdout)流演示一下
③fputs与fgets
1.fputs——输出函数
<功能>将字符串写入流中
<返回值>int——非负值表示成功
EOF——发生错误
<适用>所有流
2.fgets——输入函数
<功能>从流中获取字符串
<返回值>正常——返回字符串
NULL——表示错误或者到达文件结尾
<参数>n-从流中读取的最大字符数(\0会自动占去一位)
3.使用示范
#include <stdio.h> #include <string.h> #include<errno.h> //fputs int main() { FILE*pw = fopen("test.txt","w"); if (pw == NULL) { printf("%s",strerror(errno)); return 0; } char ch[5] = "abcd"; fputs(ch,pw); fclose(pw); pw = NULL; return 0; } //fgets int main() { FILE*pr = fopen("test.txt","r"); if (pr == NULL) { printf("%s",strerror(errno)); return 0; } char ch[5] = {0}; fgets(ch,3,pr);//有一位自动被\0占用 printf("%s",ch); return 0; }
④fprintf与fscanf
1.fprintf——输出函数
<功能>将特定格式的数据写入流中
<返回值>返回打印的字节数。
<适用>所有流
<对比>printf默认将数据打印在标准输出流(stdout)上,而fprintf的输出流可以选择。printf 打印的字符数返回
2.fscanf——输入函数
<功能>从流中读取特定格式的数据
<适用>所有流
<返回值>返回成功转换和分配的字段数量
3.使用示范
//头文件同上略 //fprintf int main() { FILE*pw = fopen("test.txt","w"); if (pw == NULL) { printf("%s",strerror(errno)); return 0; } fprintf(pw,"%d %.2f %c",10,3.14,'a'); fclose(pw); pw = NULL; return 0; } //fscanf int main() { int a; float b; char c; FILE*pr = fopen("test.txt","r"); if (pr == NULL) { printf("%s",strerror(errno)); return 0; } fscanf(pr,"%d %f %c",&a,&b,&c); printf("%d %.2f %c", a,b,c); return 0; }
⑤fwrite与fread
1.fwrite——输出函数
<功能>将数据以二进制的形式写入文件流中
<参数>buffer-指向写入数据的指针 size-每一个元素的大小 count-写入的最大元素数
<返回值>实际写入的元素数
<适用>文件流
2.fread——输入函数
<功能>将数据以二进制的形式从文件流中读取
<返回值>实际读入的元素数
<适用>文件流
3.使用示范
//头文件同上略 //fwrite fread typedef struct stu { int n; float score; char name[10]; }stu; int main() { stu s = {10,100.0,"张三"}; FILE*pr = fopen("test.txt","rb"); struct stu s1 = { 0 }; if (pr == NULL) { printf("%s",strerror(errno)); return 0; } //fwrite(&s,sizeof(stu),1,pw); fread(&s1,sizeof(stu),1,pr); fclose(pr); pr = NULL; return 0; }
⑥fseek与ftell与rewind
1.fseek
<功能>将文件指针移到指定的位置
<参数>offset-偏移量 origin-初始化文件指针位置
origin有三种选择:SEEK_CUR 从当前位置开始
SEEK_END 从文件结尾开始
SEEK_SET 从文件开头开始
<返回值>成功返回0,失败返回非0值
<注意>对文件指针的修改不可以通过p++的方式实先
使用演示(现已知记事本储存数据为“abcdef”)
//fseek int main() { FILE*pr = fopen("test.txt","r"); int a = 0; if (pr == NULL) { printf("%s",strerror(errno)); return 0; } //定位文件指针 fseek(pr, -1, SEEK_END);//指向最后一个的后一个 a=fgetc(pr); b=ftell(pr);//返回当前指针位置 printf("%c",a); printf("%d",b); fclose(pr); pr = NULL; return 0; }
(若初始化文件指针为SEEK_SET,偏移1则得到b)
2.ftell
<功能>返回当前文件指针的偏移量
3.rewind
<功能>使文件指针回到文件的开始
使用演示
int main() { int n; FILE * pFile; char buffer[27]; pFile = fopen("test.txt", "w+"); for (n = 'A'; n <= 'Z'; n++) fputc(n, pFile); rewind(pFile); fread(buffer, 1, 26, pFile); fclose(pFile); buffer[26] = '\0'; puts(buffer); return 0; }
⑦ferror与feof
1.ferror
<功能>检查流是否发生了错误
<返回值>若发生错误则返回0,否则返回非0值
2.feof
<功能>当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
<返回值>若不是文件末尾则返回0,是文件末尾则返回非0值
<注意>不能用feof函数的返回值直接用来判断文件的是否结束
1. 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets)
例如:
fgetc判断是否为EOF.
fgets判断返回值是否为NULL.
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数
使用演示(对于文本文件)
int main(void) { int c; // 注意:int,非char,要求处理EOF,EOF本质上是-1 FILE* fp = fopen("test.txt", "r"); if (!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环 { putchar(c); } //判断是什么原因结束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
使用演示(对于二进制文件)
enum { SIZE = 5 }; int main(void) { double a[SIZE] = { 1.0, 2.0, 3.0, 4.0, 5.0 }; double b = 0.0; size_t ret_code = 0; FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式 fwrite(a, sizeof(*a), SIZE, fp); // 写 double 的数组 sizeof(*)表示这种类型大小 fclose(fp); fp = fopen("test.bin", "rb"); // 读 double 的数组 while ((ret_code = fread(&b, sizeof(double), 1, fp)) >= 1) { printf("%lf\n", b); } if (feof(fp)) printf("Error reading test.bin: unexpected end of file\n"); else if (ferror(fp)) { perror("Error reading test.bin"); } fclose(fp); fp = NULL; }
⑧补充函数 sscanf sprintf
1.sprintf
<功能>将特定格式的数据写入字符串
<参数>buffer-输出的储存位置
<返回值>写入字符串的数据大小(单位字节)
2.sscanf
<功能>从字符串中读取特定格式的字符串
3.使用演示
//sprintf sscanf typedef struct stu { int n; float score; char name[10]; }stu; int main() { stu s = {10,3.14,"张三"}; char buf1[1024] = {0}; stu s1 = {0}; sprintf(buf1, "%d %f %s",s.n,s.score,s.name);//不要再加上n=...,score=... sscanf(buf1,"%d %f %s",&(s1.n),&(s1.score),&(s1.name)); printf("%d\n",s1.n); printf("%s\n",s1.name); return 0 ; }
⑨补充函数perror strerror
<比较>perror和strerror相比不需要引用errno变量,也不需要使用printf函数,操作更加便捷。同时自行输入的字符串起到标识的作用,不会混淆。
总结
加载全部内容