目录
概念
文件名
文件的打开和关闭
fopen
fclose
输入输出函数
fputc
fgetc
fputs
fgets
fprintf
fscanf
fwrite
fread
三种流
scanf和sprintf
结构体转化
编辑
文件的随机读写
fseek
ftell
rewind
文本文件和二进制文件
文件读取结束的判定
文件缓冲区
概念
文件的作用:文件是存放于硬盘中的,使数据能够持久化保存。
数据文件:文件的内容不一定是程序,它可以是程序运行时产生的读写数据。
有时后我们会将文件输出到磁盘上,在需要的时候再从磁盘上把数据读取到内存中使用。
文件名
一个文件需要一个唯一的文件标识:
文件路径 + 文件名主干 + 文件后缀例: c:\code\test.txt
文件的打开和关闭
为了方便访问文件中的数据,每个被使用的文件都在内存中开辟了一个相应的文件信息区,记录文件名和文件的状态、文件路径等)。这个文件信息区被存放在一个结构体变量中,通过文件指针可以访问这块区域,并对文件实施各种操作。
FILE* pf;//文件指针变量
fopen
根据文件名打开打开文件,并返回一个FILE*类型的指针。
文件操作模式类型(第二个参数):
注意文件路径可以是相对或绝对路径
fclose
关闭文件,和fopen一起使用。
输入输出函数
fputc
意为将字符写入到文件中。
返回值: 为字符的ASCII码值(非负值)。失败返回-1。
利用返回值将写入的数据依次打印到屏幕上:
int main()
{
//打开文件
FILE* pf = fopen("text.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//写文件
int i = 0;
for (i = 0; i < 26; i++)
{
putchar(fputc('a'+i, pf));
}
fclose(pf);
pf = NULL;
return 0;
}
fgetc
意为获取文件中的字符并返回它的ascii码值。在使用fgetc前,要确保文件状态为 r 专注输入数据而非w专注输出数据,否则会导致读取失败。
pf指针具有Placeholder指针可以遍历文件:
int main()
{
//打开文件
FILE* pf = fopen("text.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
int ch = 0;
while ((ch = fgetc(pf)) != EOF)//意为读取文件所有内容之后返回-1(文件末尾)
{
printf("%c", ch);
}
fclose(pf);
pf = NULL;
return 0;
}
fputs
作用:将字符串写入到文件中,想达到按行写入可以主动加上\n。
返回值:成功时返回非负值,失败返回-1。
fgets
作用:从文件中依次读取字符串,遇到\n或者读取(num-1)个数字后终止读取。也就是说它只读取num-1个数字并在num或\n后处自动补上\0。
返回值:str的首地址,若失败返回NULL。
fprintf
从文件流中按照一定格式写数据。
例如复杂的结构体:
struct S
{
char name[20];
int age;
float score;
};
fscanf
从文件流中按照一定格式读数据。
当然,你也可以用前面的文件读取函数进行读取:
char arr[20] = { 0 };
fgets(arr,20,pf);//也行
printf("%s",arr);
fwrite
对文件进行二进制写入。[适用于所有类型]
第一个参数为写入数据的地址,第二个参数为数据的大小,第三个参数为数据个数。类似于memcpy一个字节一个字节拷贝数据。
注意读写二进制文件属性要变为 ”wb"
这下知道文件中为什么会有乱码了吧~
fread
对文件进行二进制读取,会返回读取成功的count个数。
三种流
而文件输入输出函数适用于所有流,它们对数据的操作可以不局限于文件。也就是说可以用它们实现像scanf、printf之类函数的功能:
fprintf(stdout, "hello world");//屏幕
int a = 0;
fscanf(stdin,"%d" ,a);//键盘
scanf和sprintf
sscanf是按照一定格式将字符串转换为特定格式。
sprintf是将其他数据类型按照指定的格式转换成字符串。【后面自动补‘\0’】
结构体转化
将结构体转化成字符串:
将字符串转化成结构体:
文件的随机读写
fseek
作用:将流里文件指针设置起始位置和偏移量,按此偏移量的位置进行读写操作。
SEEK_END为末尾数据的下一个位置
int ch = fgetc(pf);
putchar(ch);
//a
fseek(pf,-2,SEEK_END);//反向找
ch = fgetc(pf);
putchar(ch);
//e
fseek(pf, 0, SEEK_SET);//回到起始位置
ch = fgetc(pf);
putchar(ch);
//a
//查找完a后指针位置会向后增1
fseek(pf, 3, SEEK_CUR);//b-->cde
ch = fgetc(pf);
putchar(ch);
//e
ftell
返回当前文件指针所指向的位置(偏移量)。
rewind
将文件指针设置成最开始的位置。
文本文件和二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是 二进制文件 。(乱码)字符一律以 ASCII 形式存储,数值型数据既可以用 ASCII 形式存储,也可以使用二进制形式存储。
对于整数10000,以ASCII码存储为5个字节,而二进制存储只占4和字节。
文件读取结束的判定
牢记:在文件读取过程中,不能用 feof 函数的返回值直接用来判断文件的是否结束。而是 应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
- fgetc 判断是否为 EOF .
- fgets 判断返回值是否为 NULL .
- fread判断返回值是否小于实际要读的个数。
fgetc:
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF)
{
putchar(c);
}
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二进制:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else {
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
文件缓冲区
所谓文件缓冲区,就是在使用文件时,系统会为每个文件分配一块缓冲区用于存放数据,当内存向磁盘中传输数据或当磁盘向内存中传输数据的时候,这些数据会一个一个被加载到缓存区上,再一齐被加载到内存或磁盘上。
作用:将缓存区中的数据输出到流里(stdout、FILE)
#include <windows.h>
#include <stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
int a = 10000;
fwrite(&a, sizeof(int), 1, pf);//二进制
printf("内容存放在缓冲区,此时打开文件无内容\n");
Sleep(10000);//休眠10秒,数据已经在缓存区上了
fflush(pf);//刷新缓冲区
printf("再打开文件,内容已存在\n");
fclose(pf);//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
}
注:读取写入数据时遇到\n或fclose也会刷新缓冲区。
所以在我们对文件进行操作时别忘了执行刷新缓冲区和文件关闭操作!否则可能造成文件读写出错。