目录
对文件的初步认识:
1.文件作用:使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
2.什么是文件:文件可以是文本文档、图片、程序等等。总的来说,电脑磁盘上的就是文件。程序设计中,文件有两种:程序文件、数据文件
程序文件:
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。程序文件存储的是程序,包括源程序和可执行程序。
数据文件:
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
程序文件和数据文件间的关系:
文件名:
文件名便于我们识别不同的文件,文件名包含3部分:文件路径+文件名主干+文件后缀
例如:D:\day\test.txt
1.文件的打开和关闭
1.1文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息
这些信息保存在结构体变量中,该结构体类型是有系统声明的,取名FILE(注:不同的C编译器的FILE类型包含的内容不完全相同)
打开一个文件的时候,系统会自动创建一个FILE结构的变量,并填充其中的信息,一般都是通过一个FILE的指针来维护这个FILE结构的变量
FILE* pf;//文件指针变量 //定义pf是一个指向FILE类型数据的指针变量。 //可以使pf指向某个文件的文件信息区(是一个结构体变量)。
大致意思就是:文件信息区相当于一个结构体变量的集合,整体是结构体类型被系统typedef成 FILE类型专门针对文件操作,FILE* 指针专门用来对文件进行操作,结构体内容会在使用时自动创建,我们只需要创建文件指针变量进行恰当的使用可。
1.2文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
编写程序时,打开文件的同时,会返回一个FILE*的指针变量指向该文件,因此在打开文件的同时,需要有一个FILE 类型的指针来接收。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件//打开文件 FILE * fopen ( const char * filename, const char * mode ); //关闭文件 int fclose ( FILE * stream );
fopen("文件名+后缀","打开方式"),fclose(指针变量)
打开方式:
前三种为常用,需掌握。
文件使用方式 含义 如果指定文件不存在 “r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错 “w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件 “a”(追加) 向文本文件尾添加数据 建立一个新的文件 “rb”(只读) 为了输入数据,打开一个二进制文件 出错 “wb”(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件 “ab”(追加) 向一个二进制文件尾添加数据 出错 “r+”(读写) 为了读和写,打开一个文本文件 出错 “w+”(读写) 为了读和写,建议一个新的文件 建立一个新的文件 “a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件 “rb+”(读写) 为了读和写打开一个二进制文件 出错 “wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件 “ab+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件 打开关闭举例:
#include <stdio.h> int main() { FILE * pFile; //打开文件 pFile = fopen("myfile.txt", "w"); //指针接收 //文件操作 if (pFile != NULL) //判断是否创建成功,失败返回NULL { fputs("fopen example", pFile); //向创建的文件输入字符串 //关闭文件 fclose(pFile); pFile = NULL; //指针置空 } return 0; }
如果想打开其他路径下的文件,则须在原基础上加上路径,并且若已存在的文件内有内容,会自动清空:
fopen("D:\\C code\\C-code\\myfile.txt", "w")
这里注意使用\\ ,防止转义字符进行转义。
程序运行时,会在程序相应路径下创建相应文件 并对其进行操作,之后关闭文件,结束程序。
2.对文件的操作函数
功能 函数名 适用于 字符输入函数 fgetc 所有输入流 字符输出函数 fputc 所有输出流 文本行输入函数 fgets 所有输入流 文本行输出函数 fputs 所有输出流 格式化输入函数 fscanf 所有输入流 格式化输出函数 fprintf 所有输出流 二进制输入 fread 文件 二进制输出 fwrite 文件
重要概念:
输入是从键盘或文件等向内存输入;输出是从内存向屏幕、文件等写入。
1.符输出函数-fputc
fputc ( 要写入的字符 , 指向文件的指针 )
一个字符一个字符的向文件写入。
举例:
#include <stdio.h> #include <string.h> #include <errno.h> int main() { FILE * pf; //打开文件 pf = fopen("myfile.txt", "w"); if (pf == NULL) { printf("%s\n",strerror(errno)); } //文件操作 for (char pa = 'a'; pa <= 'z'; pa++) { fputc(pa,pf); } //关闭文件 fclose(pf); pf = NULL; return 0; }
对流的拓展:
C语言程序在运行时,会默认打开stdin(默认输入流)、stdout(标准输出流)、stderr(标准错误流),我们对文件操作时,需要打开文件关闭文件,但运行程序时却不需要打开关闭屏幕,就是因为标准流得存在。可以这样想,在操作文件时,若想将内容输出到屏幕上,则可以使用标准输出流。
例如:
for (char pa = 'a'; pa <= 'z'; pa++)
{
fputc(pa,stdout);
}
2.字符输入函数-fgetc
int fgetc(指向文件的指针) ,是int类型,此函数的返回值为对应字符的ASCII码值,且使用一次,则指针向后移动一位,指向下一个字符。错误或结束时返回EOF。
举例:
int main() { FILE * pf; //打开文件 pf = fopen("myfile.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); } //文件操作 int ret=fgetc(pf); printf("%c\n",ret); ret = fgetc(pf); printf("%c\n", ret); ret = fgetc(pf); printf("%c\n", ret); //关闭文件 fclose(pf); pf = NULL; return 0; }
3.文本行输出函数 -fputs
可以参考字符输出函数fputc。
举例:
int main() { FILE * pf; //打开文件 pf = fopen("myfile.txt", "w"); if (pf == NULL) { printf("%s\n", strerror(errno)); return 0; } //文件操作 fputs("hello\n",pf); fputs("world\n",pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
4.文本行输入函数 -fgets
此函数返回值为指针,有三个参数,第一个为接收的数组指针,第二个为最大的读取个数,第三个为指向文件的指针。遇到结束或错误返回NULL。
举例:
小实践-文件拷贝
目的:
实现将一个文件进行拷贝获得第二份文件
思路:
1.打开两个文件,源文件和一个复制文件
2.判断是否成功打开
3.对源文件进行读取,输入复制文件中
4.关闭文件
实践:
int main()
{
//实现一个代码将myfile.txt拷贝一份生成myfile2.txt
//第一、二步
FILE* pr = fopen("myfile.txt","r");
if (pr==NULL)
{
printf("open for readingt:%s\n",strerror(errno));
}
FILE* pw = fopen("myfile2.txt","w");
if (pw==NULL)
{
printf("open for writting:%s\n",strerror(errno));
fclose(pr); //源文件成功打开,复制文件失败
pr = NULL; //则关闭源文件,置空指针
return 0;
}
//第三步,拷贝文件
int ch = 0;
while ((ch=fgetc(pr))!=EOF) //一个字符一个字符进行拷贝
{
fputc(ch,pw);
}
//第四步
fclose(pr);
pr = NULL;
fclose(pw);
pw = NULL;
return 0;
}
5.格式化输出函数-fprintf
将结构体数据写入文件。
fprintf 比 printf 多一个参数,即第一个指向文件的指针
举例:
struct Book { char name[12]; int price; double word; }; int main() { struct Book b = {"程序设计",30,87.5}; FILE* pf = fopen("data.txt","w"); if (pf == NULL) { printf("%s\n",strerror(errno)); } //写格式化数据 fprintf(pf,"%s %d %lf",b.name,b.price,b.word); fclose(pf); pf = NULL; return 0; }
6.格式化输入函数-fscanf
与scanf相差一个参数,用法与fprintf相似
将文件内容输入内存
举例:
struct Book { char name[12]; int price; double word; }; int main() { struct Book b = { 0 }; FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); } //读格式化数据 fscanf(pf,"%s %d %lf",b.name,&(b.price),&(b.word)); printf("%s %d %lf",b.name,b.price,b.word); fclose(pf); pf = NULL; return 0; }
7.二进制输出-fwrite
第一个参数为要被写入的数据, 第二个参数为每个元素的大小,第三个参数为元素个数,第四个参数为指向文件的指针。
举例:
struct Book { char name[12]; int price; double word; }; int main() { struct Book b[2] = { { "程序设计", 30, 87.5 },{"理解c",15,50.5} }; FILE* pf = fopen("data.txt", "wb"); if (pf == NULL) { printf("%s\n", strerror(errno)); } //按照二进制写入文件 fwrite(&b,sizeof(struct Book),1,pf); fclose(pf); pf = NULL; return 0; }
以二进制的形式放入文本中,无法解析。
8.二进制输入-fread
参数与fwrite相同,用法相似。
举例:
//依照7的例子,改动文件操作部分 //按照二进制写入文件 fread(&b, sizeof(struct Book), 1, pf); printf("%s %d %lf",b[0].name,b[0].price,b[0].word);
二进制形式放入内存中打印显示可以看明白。
3.sscanf和sprintf
3.1 sprintf
与printf对比也只是多个参数,此函数的作用是将把一个格式化的数据转换成字符串。
举例:
struct Book { char name[12]; int price; double word; }; int main() { struct Book b = { "程序设计", 30, 87.5 }; //写入文件 char buf[100] = { 0 }; sprintf(buf,"%s %d %lf", b.name, b.price, b.word); printf("%s\n",buf); return 0; }
3.2 sscanf
此函数是从字符串中提取出格式化的数据,与sprintf是成对使用。
举例如下:
struct Book { char name[12]; int price; double word; }; int main() { struct Book b = { "程序设计", 30, 87.5 }; struct Book b2 = { 0 }; //写入文件 char buf[100] = { 0 }; sprintf(buf,"%s %d %lf", b.name, b.price, b.word); printf("%s\n",buf); sscanf(buf, "%s %d %lf", b2.name,&( b2.price),&( b2.word)); printf("%s %d %lf\n", b2.name, b2.price, b2.word); return 0; }
4.文件的随机读写
4.1-fseek
根据文件指针的位置和偏移量来定位指针
第一个参数为指针,第二个为偏移量,第三个为偏移的起始位置。
举例:
int main() { FILE* pf=fopen("test.txt","r");//提前创建文本abcdef if (pf==NULL) { printf("%s\n",strerror(errno)); return 0; } //读文件 int ch = fgetc(pf); printf("%c\n", ch); //打印a后向后一位,此时指针指向b //定位文件指针 fseek(pf, 2, SEEK_CUR);//SEEK_CUR当前位置,向后两位 ch = fgetc(pf); printf("%c\n", ch); //此时应是d fseek(pf, -1, SEEK_END);//SEEK_END文件末尾,向前一位 ch = fgetc(pf); printf("%c\n", ch); //此时应是f fseek(pf, 2, SEEK_SET);//SEEK_SET文件起始,位置向后两位 ch = fgetc(pf); printf("%c\n", ch); //此时应是c fclose(pf); pf = NULL; return 0; }
4.2-ftell
以上述文本为例,此函数返回的是偏移量。
int ch = fgetc(pf); printf("%c\n", ch); //打印a后向后一位,此时指针指向b int ret = ftell(pf); //返回的是指针从起始位置开始的偏移量 printf("%d\n",ret); // 1
4.3-rewind
此函数的作用:将指针返回文件的起始位置。
rewind(pf); //返回起始位置,指向a ch = fgetc(pf); printf("%c\n", ch); //打印a
5.判断文件结束的原因-feof
注意:不能用feof来判断文件是否读取结束,它的正确用法是当文件结束是判断文件结束的原因是读取失败结束还是遇到文件尾结束。
//如果上述有需要判断的读取是否结束 //例如:fgetc 判断是否为 EOF // fgets 判断返回值是否为 NULL if (ferror(fp)) puts("I/O error when reading"); //读取失败结束 else if (feof(fp)) puts("End of file reached successfully"); //遇到文件末尾结束
ferror和feof两个函数返回值为真,代表着两种失败原因,执行的前提是该文件已经读取结束。
6.文件缓冲区
为了提高程序运行效率,系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。缓冲区的大小根据C编译系统决定的。
程序运行时,要输入或输出的数据都会先在缓冲区存放,当存满时再存入文件或者内存中。
举个例子:好比搬砖
一辆车从厂里向工地里搬,一辆车一次搬10块砖,来回好多次搬完,但效率太低了;让车厢装满再运过去,可能两三次就搬完了,这样效率就会高很多了。
注意:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区(将缓冲区的数据输出)或者在文件操作结束的时候关闭文件。