在上篇博文中介绍到了文件的打开fopen和关闭fclose以及文件指针FILE*,在这边博文中将介绍文件的读写
函数名 | 功能 | 适用于 |
---|---|---|
fgetc | 字符输入函数 | 所有输入流 |
fputc | 字符输出函数 | 所以输出流 |
fgets | ⽂本⾏输⼊函数 | 所以输入流 |
fputs | ⽂本⾏输出函数 | 所以输出流 |
fscanf | 格式化输入函数 | 所以输入流 |
fprintf | 格式化输出函数 | 所以输出流 |
fread | 二进制输入 | 文件 |
fwrite | 二进制输出 | 文件 |
所有输入流/所有输出流:适⽤于标准输⼊/输出流和其他输⼊/输出流(如⽂件输⼊/输出流)
字符输入/输出函数:用于单个字符的输入/输出
fgetc():从文件中读取字符
int fgetc ( FILE * stream );
fputc():将字符写入文件
int fputc ( int character, FILE * stream );
示例如下:
#include
#include
int main()
{
FILE* pfwrite = fopen("test.txt", "w"); //以只写的方式打开文件
if (pfwrite == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
fputc('a', pfwrite); //将‘a’放入文件
fclose(pfwrite); //关闭文件
pfwrite = NULL; //文件指针置NULL
FILE* pfread = fopen("test.txt", "r"); //以只读的方式打开文件
if (pfread == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
int ch = fgetc(pfread); //将读取的字符存放到 ch
printf("%c", ch); //打印ch
fclose(pfread); //关闭文件
pfread = NULL; //文件指针置NULL
return 0;
}
注:每次打开同一个文件时,无论是否有内容写入,文件的内容会被覆盖
⽂本⾏输⼊/输出函数:用于字符串的输入/输出
fgets():从文件中读取 num 个长度的字符
char * fgets ( char * str, int num, FILE * stream );
fputs():将字符串写入文件
int fputs ( const char * str, FILE * stream );
示例如下:
#include
#include
int main()
{
FILE* pfwrite = fopen("test.txt", "w"); //以只写的方式打开文件
if (pfwrite == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
fputs("abcdef\n", pfwrite); //将"abcdef\n"放入文件
fclose(pfwrite); //关闭文件
pfwrite = NULL; //文件指针置NULL
FILE* pfread = fopen("test.txt", "r"); //以只读的方式打开文件
if (pfread == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
char str[20] = { 0 };
fgets(str,5,pfread); //将读取的字符存放到 str
printf("%s\n", str); //打印str
fclose(pfread); //关闭文件
pfread = NULL; //文件指针置NULL
return 0;
}
注:当我们写入一串字符串时,如果不主动换行,会在同一行写入,所有如果我们要换行的话,我们可以在输入的字符串中加上个\n
格式化输入/输出函数:用于按格式将数据输出/输入文件
fscanf():将数据按格式读取文件
int fscanf ( FILE * stream, const char * format, ... );
int scanf ( const char * format, ... );
fprintf():将数据按格式的写入文件
int fprintf ( FILE * stream, const char * format, ... );
int printf( const char * str, ...);
fscanf()/fprintf()和正常的scanf()/printf()用法一样,不过是从文件中读取/写入
示例如下:
#include
#include
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s1 = { "zhangsan",18,92.3f };
struct Stu s2 = { 0 };
FILE* pfwrite = fopen("test.txt", "w"); //以只写的方式打开文件
if (pfwrite == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
fprintf(pfwrite,"%s %d %f",s1.name,s1.age,s1.score); //将s1中的内容放入文件
fclose(pfwrite); //关闭文件
pfwrite = NULL; //文件指针置NULL
FILE* pfread = fopen("test.txt", "r"); //以只读的方式打开文件
if (pfread == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
fscanf(pfread, "%s %d %f", s2.name, &(s2.age), &(s2.score)); //将读取的字符存放到 s2
fprintf(stdout, "%s %d %.2f", s2.name, s2.age, s2.score); //使用fprintf将s2中的内容打印到屏幕
fclose(pfread); //关闭文件
pfread = NULL; //文件指针置NULL
return 0;
}
注:stdout为标准输出流(屏幕)stdin为标准输入流(键盘)stderr为标准错误流(屏幕)
二进制输入/输出:将二进制输入/输出到文件(只针对文件)
fread():从文件中读取count个大小为size的数据,存放在ptr指向的数据
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
fwrite():将ptr指向的数据,写入count个大小为size到文件
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
示例如下:
#include
#include
int main()
{
int a = 10000;
int b = 0;
FILE* pfwrite = fopen("test.txt", "wb"); //以只写的方式打开文件
if (pfwrite == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
fwrite(&a,sizeof(int),1,pfwrite); //将a中的数据以二进制形式写入文件
fclose(pfwrite); //关闭文件
pfwrite = NULL; //文件指针置NULL
FILE* pfread = fopen("test.txt", "rb"); //以只读的方式打开文件
if (pfread == NULL) //判断是否打开成功
{
perror("fopen"); //如果打开失败根据错误码打印错误信息
return 1; //关闭程序
}
fread(&b,sizeof(int),1,pfread); //将读取的数据放到 b
printf("%d", b);
fclose(pfread); //关闭文件
pfread = NULL; //文件指针置NULL
return 0;
}
注:写入记事本中的二进制数据是不能直接看出来的,但是无论放入还是取出来都是10000,所以不用过度关注
在文件中,写入一个数据,文件指针就向后走,那我们要如何在指定位置写入或读取数据呢?
下面来介绍三个函数fseek ftell rewind
fseek:为根据文件指针的位置,向前或向后移动文字指针
int fseek ( FILE * stream, long int offset, int origin );
offset:为偏移量,向前或向后或不动
origin :为文件的位置
关于origin C语言中规定有三个取值
- SEEK_SET 文件的起始位置
- SEEK_CUR 文件指针的当前位置
- SEEK_END 文件的末尾
示例如下:
#include
#include
int main()
{
FILE* pf = fopen("test.txt", "r");
if (!pf)
{
perror("fopen");
return -1;
}
//假设文件中已经有字符了 "abcdef"
int ch = fgetc(pf);
printf("%c\n", ch); //打印'a'
ch = fgetc(pf);
printf("%c\n", ch); //打印'b'
ch = fgetc(pf);
printf("%c\n", ch); //打印'c'
ch = fgetc(pf);
printf("%c\n", ch); //打印'd'
//三种方式都可以
//fseek(pf, -4, SEEK_CUR);//当前位置'd',倒退4个字节,'a'
//fseek(pf, 0, SEEK_SET);//当前位置'd',文件起始位置
fseek(pf, -6, SEEK_END);//当前位置'd',文件末尾位置,倒退6个字节'a'
ch = fgetc(pf);
printf("%c\n", ch); //打印'a'
fclose(pf);
pf = NULL;
return 0;
}
ftell:为相对文件起始位置的偏移量
long int ftell ( FILE * stream );
#include
#include
int main()
{
FILE* pf = fopen("test.txt", "r");
if (!pf)
{
perror("fopen");
return -1;
}
//假设文件中已经有字符了 "abcdef"
int ch = fgetc(pf);
printf("%c\n", ch); //打印'a'
ch = fgetc(pf);
printf("%c\n", ch); //打印'b'
ch = fgetc(pf);
printf("%c\n", ch); //打印'c'
ch = fgetc(pf);
printf("%c\n", ch); //打印'd'
ch = fgetc(pf);
printf("%c\n", ch); //打印'e'
int ret = ftell(pf); //打印了5次,相对文件起始位置5个字节
printf("%d",ret);
fclose(pf);
pf = NULL;
return 0;
}
frewind:为回到文件的起始位置
void rewind ( FILE * stream );
#include
#include
int main()
{
FILE* pf = fopen("test.txt", "r");
if (!pf)
{
perror("fopen");
return -1;
}
//假设文件中已经有字符了 "abcdef"
int ch = fgetc(pf);
printf("%c\n", ch); //打印'a'
ch = fgetc(pf);
printf("%c\n", ch); //打印'b'
ch = fgetc(pf);
printf("%c\n", ch); //打印'c'
ch = fgetc(pf);
printf("%c\n", ch); //打印'd'
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch); //打印'a'
fclose(pf);
pf = NULL;
return 0;
}
在文件读取过程中,不能使用feof函数的返回值直接判断文件是否结束
feof:为在文件读取结束的时候,判断读取结束的原因是否是遇到文件尾结束
ferror:为在文件读取结束的时候,判断读取结束的原因是否是遇到错误结束
使用feof判断不同文件读取结束的方法
feof/ferror使用示例如下:
文本文件:
#include
#include
int main(void)
{
int c = 0;
FILE* fp = fopen("testc.txt", "r");
if (!fp)
{
perror("fopen");
return 1;
}
//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF)
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
perror("error"); //文件遇到错误结束,则打印错误信息
else if (feof(fp))
printf("success"); //文件正常结束,成功则打印success
fclose(fp);
}
二进制文件:
#include
int main()
{
int a = 10;
int b = 0;
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//fwrite(&a, sizeof a, 1, pf);
size_t ret = fread(&b, sizeof b, 5, pf);
if (ret == 5) //当返回值等于读取的个数表示成功
{
printf("success");
}
else
{
if (ferror(pf)) //判断是否遇到错误
{
perror("ferror");
}
else if(feof(pf)) //判断是否文件结束
{
printf("feof");
}
}
fclose(pf);
pf = NULL;
return 0;
}
ANSIC 标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的
注:当我们写入或读取文件的时候,并不是立刻完成的,而且将要写入或读取的数据放在文件缓冲区,等缓冲区满了或者刷新缓冲区时(fflush fclose),才会读取或写入数据
#include
#include
//VS2022 WIN11环境测试
int main()
{
FILE*pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)
//注:fflush 在⾼版本的VS上不能使⽤了
printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭⽂件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}