前言:
本文主要是介绍关于文件的相关知识,可以增进对文件——这个电脑上随处可见的元素的了解。
为什么使用文件?
我们平时使用电脑,实际上是对电脑的数据进行一系列处理,比如读取、写入等基础操作。在这些数据当中,既有电脑中的固有数据,也有从网络上传输来的数据。这些数据记录着各种各样的信息,因此需要分类;如果需要保存数据,还要将数据持久化而避免被删除。
所以,文件就出现了。使用文件可以将数据分开地保存在电脑的硬盘中,很好地做到了数据的持久化和分类。
文件的基本知识:
在程序设计中,会按照文件的功能将其分类为程序文件和数据文件。
下面我就会分别介绍这两类文件。
程序文件:
对于C语言而言,程序文件由以下部分组成:
- 程序源文件(后缀为.c C++后缀为.cpp)
- 目标文件(在windows环境中后缀为.obj)
- 可执行程序(在windows环境中后缀为.exe)
数据文件:
文件的内容不一定是程序的源代码,还可以是程序运行时所读写的数据。
在了解文件操作的知识之前,程序运行时的数据读写都是以终端为对象,也就是作为终端的键盘输入数据再传给作为终端的显示器。在这个过程中,没有将传入的数据保存到硬盘。
所谓的数据文件,就是在数据输出的对象为硬盘时所产生的文件。
文件名:
文件保存在硬盘中,正如程序中的变量在内存中,必须有一个唯一的标识正确地记录数据所在地才能使用。
文件名包含三部分:
1.文件路径
2.文件主干
3.文件后缀
用一图来表示:
程序中文件的打开和使用:
文件指针:
每个被使用的文件都会在内存中开辟相应的文件信息区来存放文件的相应信息区中(比如文件的名称、当前所在位置等,不过这些不是这篇文章所要讲解的内容),这些信息是保存在一个FILE结构体变量中。因此,如果想在程序中使用文件,就可以使用FILE指针来指向这个结构体,进而关联到我们想要的文件。
创建一个文件指针变量:
FILE* pf;
不同的文件有不同的文件信息区:
文件的打开:
在了解文件与程序的关联后,就要知道怎么通过文件指针来打开文件。
fopen():
使用fopen()来打开文件:
FILE* pf = fopen(const char* filename, const char* mode);
其中,filename为文件名,mode为模式,它们都是以字符串的形式传递给fopen。
mode是规定的好的,顾名思义fopen对于要操作的文件有很多种打开方式,其中最常用的有三种:
1.只读:“r” 打开一个文件,并将文件里的数据传输给程序。
2.只写:“w” 打开一个文件,并将程序里的数据传输给文件。
3.追加:“a” 打开一个文件,将光标放在文件尾,并添加数据。
其中要注意的有:
1、无论是只读还是只写,它们只能“单向实现”,若在只读模式中使用了可以输出的语句,那该语句无效(不报错)。
2、若指定的文件不存在,只读模式下会报错,只写和追加模式下会建立一个新文件。
3、在只写模式下,若存在指定的文件,其会将其删除并建立新文件。
还有很多模式,但本文就不补充了,有兴趣可以深入了解。
文件的关闭:
fclose():
文件的关闭很简单,看下列代码:
fclose(FILE* p);//1
p = NULL;//2
只用写上第一句就已经关闭了该指针,但该指针还存在,必须将其赋予空指针才安全。
总的流程:
//创建文件指针变量并将指定的文件以只读的形式打开
//要注意的是,如果要打开的文件不与程序所在文件在同一文件夹中,则需要补上文件路径。反之就只需要写文件主干和后缀名
FILE* pf = fopen("作文.txt", "r");
//关闭文件指针
fclose(pf);
pf = NULL;
文件的顺序读写:
在知道了文件是如何打开和关闭后,可以尝试对程序和文件的数据建立联系(也就是输入与输出)。
先来看顺序读写:
下面是顺序读写函数,这些函数会在文件打开时的光标处开始,并依照一定的规则向下进行。以下每个函数都默认文件为空文件。
fputc():
参数有两个,第一个为字符,第二个为指定的输出流(既可以是文件,也可以是显示器)
fputc('h', p);
fputc('e', p);
fputc('l', p);
fputc('l', p);
fputc('o', p);
//在文件里就是:hello
fgetc():
只有一个参数:指定的stream流,这里指的是文件
char ch = fgetc(p);
printf("%c\n", ch);
ch = fgetc(p);
printf("%c\n", ch);
ch = fgetc(p);
printf("%c\n", ch);
//到这里就会打印hel
fputs():
与fputc类似,不过第一个参数为字符串。
fputs("hello,world!!!\n", p);
fputs("C++ So hard!!!\n", p);
//文件中就是:
//hello,world!!!
//C++ So hard!!!
fgets():
fgets会读取文件中一行的数据。
第二个参数为读取当行的前n个字符,之后自动转为下一行。
char str[20] = { 0 };
fgets(str, 20, p);
printf("%s", str);
fgets(str, 20, p);
printf("%s", str);
fscanf():
从流 stream 读取格式化输入。
第一个参数为指定的流,第二个参数为格式输入符,第三个及以后为变量。
//将p指定的文件的数据按照指定类型传输给程序中的变量
fscanf(p, "%s %d %lf", a.name, &(a.age), &(a.sorce));
fprintf():
参数列表同上。
//将程序中的变量数据按照指定类型传输给p指定的文件
fprintf(p, "%s %d %.2f\n", a.name, a.age, a.sorce);
文件的随机读写:
随机读写的实现主要是将文件中的光标更改到我们想要的位置上。
fseek:
第二个参数为偏移量。
第三个参数有三个:
1、SEEK_SET 文件的开头
2、SEEK_CUR 文件指针的当前位置
3、SEEK_END 文件的末尾
//这里表示将光标变到文件开头并向后偏移6位
fseek(p, 6, SEEK_SET);
ftell:
该函数会返回当前光标的偏移量。
int size = ftell(p);
rewind:
该函数会让光标回到文件的起始位置。
rewind(p);
文件结束时的判定:
文件读取结束时有两种情况:
1、遇到文件尾结束。
2、读取文件失败。
如果想要知道是那一种情况的话,则可以使用feof。
如果是遇到文件尾结束,那fgetc会返回EOF,fgets会返回NULL。
int a = 0;
while((a = fgetc(p))!= EOF)
{
;
}
//判断是什么原因结束的:
if(ferror(p))
{
puts("error");
}
else if(feof(p))
{
puts("End Of File");
}
文件缓冲区:
在程序运行时,其中的数据与文件中的数据并不是实时地变化,而是存在一个缓冲区,在缓冲区的数据满了或有命令下来后会更新数据。
有图表示:
fflush:
手动更新数据。
struct A a = { "王老五", 50, -100 };
FILE* p = fopen("Contect.txt", "w");
fwrite(&a, sizeof(struct A), 1, p);
//睡眠10秒,此时文件中没有相应的数据
Sleep(10000);
fflush(p);
//已经刷新缓冲区,此时数据已传递到文件中
printf("已刷新");
fclose(p);
p = NULL;
结语:
本文只是简单介绍了文件相关的操作知识,可以简单地实现程序与文件的一些功能,如果有兴趣的话,可以具体了解一些函数没有说明的功能。