目录
使用文件可以将程序的数据进行存储。
一、文件的分类
程序文件
包括源文件(.c)、目标文件(.obj)、可执行程序(.exe)
数据文件
程序运行时输入、输出数据的文件
二、文件的打开与关闭
打开一个文件时,内存中会开辟空间将该文件的信息存放在一个结构体变量中,该结构体名为 FILE。
fopen
打开一个文件,返回指向该文件结构体变量的指针,打开失败则返回空指针。
当文件在该程序目录下可使用文件名打开该文件,否则要使用该文件的绝对路径。当以绝对路径打开文件时,须对路径中的每个「\」进行转义。
fclose
关闭文件的函数,文件关闭成功则返回 0,关闭失败则返回 EOF。
perror
打印程序当前错误信息的函数
例:
FILE* f = NULL;//接收函数返回值的指针
f = fopen("test.txt", "r");//打开文件进行「只读」操作
//或 f = fopen("C:\\Users\\a\\Desktop\\test\\test.txt", "r");(绝对路径)
if (f == NULL)//判断是否打开失败
{
perror("");//打印错误信息
return;//结束程序
}
fclose(f);//关闭文件
三、文件的使用方式
包含「写」功能打开文件时,会清空文件内原有内容。
使用「读写」时,若想切换文件使用方式(例:「读」完后要进行「写」),都需要使用 fflush 刷新文件缓冲区,将未传输完的数据进行传输,才能进行正常「读」或「写」。
四、流
可输入、输出数据的数据类型,FILE 类型的变量就叫做流
当使用 fopen 打开文件时,其实是将文件转换为流,从而使它可以输入、输出数据。
当程序运行时,会打开三个流:stdin —— 标准输入流(键盘)、stdout —— 标准输出流(屏幕)、stderr —— 标准错误流(屏幕)
stdio.h 表示 standard input(标准输入流)、standard output(标准输出流),这就是为什么我们在使用输入、输出函数时(如: scanf(输入)、printf(输出))的时候需要包含 <stdio.h>
既然流是由文件转换而来的,那也就是说我们可以对屏幕所对应的 stdout 进行输出操作,效果和 printf 一样。
fputs("hello world", stdout);//将字符串输出到stdout
五、读写文件
所有读写文件函数都需包含 <stdio.h>
「f」 系列的输入输出函数都作用于所有流
「输入」、「输出」都是以内存为准,比如要往文件内写数据,那就是从内存输出数据到文件,所以叫输出流;读文件时,就是将文件内数据输入到内存中,所以叫输入流。
fputc
执行成功则返回输出的字符 ASCII 码值,失败则返回 EOF
FILE* pf = fopen("test.txt", "w");//打开文件进行「只写」操作
if (pf == NULL)
{
perror("");
return;
}
fputc('a', pf);//将字符a从内存输出到文件内
//或 fputc(97,pf)//ASCII码值
fclose(pf);
fgetc
执行成功则返回输入的字符 ASCII 码值,失败则返回 EOF
char c = "";//接收读到的数据
FILE* pf = fopen("test.txt", "r");//打开文件并进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
c = fgetc(pf);//将读取到的数据放入c
printf("%c", c);//打印
fclose(pf);
fputs
执行成功则返回一个非负数,执行失败则返回 EOF
FILE* pf = fopen("test.txt", "w");//打开文件进行「只写」操作
if (pf == NULL)
{
perror("");
return;
}
fputs("hello world", pf);//将字符串从内存输出到文件内
fclose(pf);
fgets
执行成功则返回存放字符串的变量,执行失败则返回 NULL
char s[15] = { 0 };//存放读取到字符串的变量
FILE* pf = fopen("test.txt", "r");//打开文件进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
fgets(s,15,pf);//将字符串从内存输出到文件内
printf(s);
fclose(pf);
fprintf
执行成功则返回输出的字节数,执行失败则返回负数
int a = 1;
char s[5] = "abc";
FILE* pf = fopen("test.txt", "w");//打开文件进行「只写」操作
if (pf == NULL)
{
perror("");
return;
}
fprintf(pf, "%d %s", a, s);//将a、s以对应格式从内存输出到文件
fclose(pf);
return 0;
fscanf
执行成功返回成功转换和分配的字段的数量(不包括已读取但未分配的字段)。若执行失败,则返回 EOF
int a = 0;//存放读取到数据的变量
char s[5] = "";//存放读取到数据的变量
FILE* pf = fopen("test.txt", "r");//打开文件进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
fscanf(pf, "%d %s", &a, s);//以空格分隔,将读取到的数据以对应数据类型放入a、s变量中
printf("%d %s", a, s);//打印
fclose(pf);
sprintf
将数据以字符串形式输出
返回当前存储在缓冲区内数据的字节数
int n = 1;
char s[3] = "";//存放读取数据的变量
sprintf(s, "%d", n);//将n中的数据转为字符串输出到s中
printf(s);//以字符串形式打印
sscanf
将字符串转换为对应格式输入
返回成功转换和分配的字段数量,若出现错误则返回 EOF
int n = 0;//存放读取数据的变量
char s[3] = "18";
sscanf(s, "%d", &n);//将s中的数据转为整型输出到n中
printf("%d", n);//以整型形式打印
fwrite
将一条数据以二进制输出到流,返回实际输出的完整条目数量
int arr[3] = { 1,2,3 };
FILE* pf = fopen("test.txt", "w");//打开文件进行「只写」操作
if (pf == NULL)
{
perror("");
return;
}
fwrite(arr, sizeof(arr), 1, pf);//将arr从内存以二进制输出到文件
fclose(pf);
fread
将一条数据以二进制输入,返回实际输出的完整条目数量
int arr[3] = { 0 };//存放读取到数据的变量
FILE* pf = fopen("test.txt", "r");//打开文件进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
fread(arr, sizeof(arr), 1, pf);//在pf指向的流中读取1次sizeof(arr)字节的数据放入arr
printf("%d %d %d", arr[0], arr[1], arr[2]);//打印
fclose(pf);
六、文件指针
文件指针指向改文件正「读」或「写」到的位置。
fseek
调整文件指针的指向
执行成功返回 0,执行失败返回非 0 值
char c = "";//存放读取到数据的变量
FILE* pf = fopen("test.txt", "r");//打开文件进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
fseek(pf, 1, SEEK_SET);//将文件指针指向起始位置+1
c = fgetc(pf);
printf("%c", c);
fclose(pf);
ftell
获取文件指针当前指向位置相对于起始位置的偏移量
long n = 0;//接收返回值的变量(返回值类型为long)
FILE* pf = fopen("test.txt", "r");//打开文件进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
fseek(pf, 3, SEEK_SET);//将文件指针指向起始位置+3
n = ftell(pf);//获取文件位置
printf("%ld", n);//打印文件位置
fclose(pf);
rewind
将文件指针指向起始位置
long n = 0;//接收返回值的变量(返回值类型为long)
FILE* pf = fopen("test.txt", "r");//打开文件进行「只读」操作
if (pf == NULL)
{
perror("");
return;
}
fseek(pf, 3, SEEK_SET);//将文件指针指向起始位置+3
rewind(pf);//将文件指针指向起始位置
ftell(pf);//获取文件指针当前指向位置
printf("%ld", n);//打印文件位置
fclose(pf);
七、文件内数据存储类型
根据数据在文件内的存储类型可以将文件分为「文本文件」和「二进制文件」
文本文件
文件内数据以 ASCII 码值存储
二进制文件
文件内数据以二进制存储
八、判断文件读取失败的原因
文件读取失败的原因分为两种:遇到结尾、遇到错误
feof
若当前文件指针指向文件尾,说明已读完文件,返回非 0 值,否则返回 0
ferror
若当前文件指针指向文件位置发送错误,返回非 0 值,否则返回 0
例:
FILE* p = fopen("test.txt", "w+");//打开文件
char a[] = "abc";
char b[10];//存放读取数据的变量
int count = 0;//记录读取文件次数的变量
fwrite(a, 1, 4, p);//将a中的数据写入文件
fflush(p);//刷新文件缓冲区以开始读取数据
count = fread(b, 1, 5, p);//将文件内的数据读5次,每次读1字节,fread返回有效读写数据条目
count = fread(b, 1, 1, p);//这时文件已经读完了,再次进行读写
if (count != 4)//若读取到字符个数与文件中字符个数不相等
{
if (feof(p))
printf("文件已读完\n");
else if (ferror(p))
printf("读取出现错误\n");
}
fclose(p);//关闭文件
九、文件缓冲区
每一个打开的文件在内存中都会有一个文件缓冲区,用于存放输出的数据,只有当缓冲区满了,或刷新缓冲区,才会将数据进行传输,用于提高系统的工作效率,防止传输数据次数过多导致其他功能效率变低。缓冲区大小由编译器决定。
例:测试文件缓冲区
FILE* p = fopen("test.txt", "w");
fputc("a", p);//将a写入p
printf("已将数据写入缓冲区,此时文件中并无数据\n");
printf("休眠中...\n");
Sleep(10000);//休眠10秒
fflush(p);
printf("已刷新缓冲区,此时可在文件中查看到数据\n");
printf("休眠中...\n");
Sleep(10000);//休眠10秒
fputc("b", p);//将a写入p
printf("已再次追加数据到文件中,此时在文件中查看不到该数据\n");
printf("休眠中...\n");
Sleep(10000);
fclose(p);//关闭文件
printf("已将文件关闭,此时可在文件中查看到追加数据\n");
printf("休眠中...");
Sleep(10000);
p = NULL;