缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE
文件打开函数---fopen
函数声明:FILE * fopen ( const char * filename, const char * mode )
函数介绍:函数成功会返回一个文件指针来维护这个文件,失败会返回一个NULL,函数的两个参数,第一个是文件路径,如"c:\code\test.txt",第二个是文件打开的方式,如"r","w","a"等等,下图是打开方式的一些总结。
文件使用方式 | 含义 | 如果指定文件不存在 |
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
文件的关闭函数---fclose
函数声明:int fclose ( FILE * stream )
函数介绍:函数的参数是文件指针,函数成功会返回0,失败会返回EOF(-1)
函数的使用演示:
int main()
{
//打开文件
FILE* pf = fopen("test.txt","w");//这是用的相对路径---该文件在这个项目里面
//FILE* pf=fopen("C:\\code\\test.txt")//这是用的绝对路径---文件可以在任意位置
//但是要注意转义字符,要用'\'来解除转义
if (pf == NULL)
{
perror("fopen");
}
//操作文件
//...
//关闭文件
fclose(pf);
//要将指针置为空,防止野指针的使用(和 malloc 与 free 函数的用法类似)
pf = NULL;
return 0;
}
字符输入函数---fgetc
函数声明:int fgetc ( FILE * stream )
函数介绍:函数参数是文件指针,函数成功返回字符的ASCII值,失败返回EOF(包含读入错误和读到文件的末尾)
字符输出函数---fputc
函数原型:int fputc ( int character, FILE * stream )
函数介绍:函数的第一个参数是输入到文件的字符,第二个参数是文件指针,函数成功返回写入的字符,失败返回EOF(-1)
函数的使用演示:
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//只写
if (pf == NULL)
{
perror("fopen");
}
//操作文件
for (int i = 0; i < 26; i++)
fputc('a' + i, pf);
//关闭文件
fclose(pf);
pf=NULL;
//注意这里我们要关闭之后再使用,或者用fseek,ftell使文件内部的指针发生偏移
//使其回到文件起始位置,这里有一点要注意fputc等函数用一次,文件内部的指针都会移动
//我们用完fputc,文件内部的指针在文件的末尾,
//所以我们通过重新打开文件或其他的一些函数如fseek,fwind,使得文件内部指针重新回到文件开头
//打开文件
pf = fopen("test.txt", "r");//只读
if (pf == NULL)
{
perror("fopen");
}
//操作文件
for (int i = 0; i < 26; i++)
{
char ch = fgetc(pf);
printf("%c ",ch);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
scanf/fscanf/sscanf
printf/fprintf/sprintf
fscanf
函数声明:int fscanf ( FILE * stream, const char * format, ... )
函数介绍:第一个参数是stream,代表流(这里是代表所有输入流,即从哪里读入数据),后面的参数与scanf一样
fprintf
函数声明:int fprintf ( FILE * stream, const char * format, ... )
函数介绍:第一个参数是stream,代表流(这里是代表所有输出流,即数据打印到哪里),后面的参函数与printf一样
函数的使用演示:
struct S
{
char a[20];
int b;
float c;
};
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//只写
if (pf == NULL)
{
perror("fopen");
}
struct S s1 = { "abcd",12,3.14f }, s2 = { 0 };
//操作文件
fprintf(pf, "%s %d %f", s1.a, s1.b, s1.c);
//关闭文件
fclose(pf);
pf = NULL;
//打开文件
pf = fopen("test.txt", "r");//只读
if (pf == NULL)
{
perror("fopen");
}
//操作文件
fscanf(pf, "%s %d %f", (s2.a), &(s2.b), &(s2.c));
printf("%s %d %f", s2.a, s2.b, s2.c);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
当然,这两个函数也能实现scanf和printf函数的作用
struct S
{
char a[20];
int b;
float c;
};
int main()
{
struct S s = { 0 };
fscanf(stdin, "%s %d %f", (s.a), &(s.b), &(s.c));
fprintf(stdout, "%s %d %f", s.a, s.b, s.c);
return 0;
}
sprintf
函数声明: int sprintf ( char * str, const char * format, ... )
函数介绍:函数的作用是将格式化的数据转化成字符串,第一个参数是数组地址,后面的参数与printf一样
sscanf
函数声明:int sscanf ( const char * s, const char * format, ...)
函数介绍:函数的作用是将字符串转化成格式化的数据第一个字符串是字符串的地址,后面的参数与scanf一样
函数使用演示:
struct S
{
char a[20];
int b;
float c;
};
int main()
{
struct S s1 = { "abc",10,3.14 }, s2 = { 0 };
char arr[50] = { 0 };
sprintf(arr, "%s %d %f", s1.a, s1.b, s1.c);
printf("%s\n",arr);
sscanf(arr, "%s %d %f", (s2.a), &(s2.b), &(s2.c));
printf("%s %d %f", s2.a, s2.b, s2.c);
return 0;
}
总结:
fread
函数声明:size_t fread ( void * ptr, size_t size, size_t count, FILE * stream )
函数介绍:从文件中读取数据,第一个参数是数据要储存的位置,第二个参数是元素的大小,第三个是读取元素的个数,第四个参数是流(输入流,即从哪里读取数据),函数返回读取到的元素个数
fwrite
函数声明:size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )
函数介绍:向文件中写入数据,第一个参数是数据所在地址,第二个参数是元素的大小,第三个是读取元素的个数,第四个参数是流(输出流,即向哪里写入数据),函数返回写入的元素个数
rewind
函数声明:void rewind ( FILE * stream)
函数介绍:将文件的指针重新回到头
函数的使用演示:
struct S
{
char a[20];
int b;
float c;
};
int main()
{
struct S s = { "zhangsan",12,3.14 },s1 = { 0 };
FILE* pf = fopen("test.txt", "w+");
fwrite(&s,sizeof(s),1,pf);
rewind(pf);
fread(&s1,sizeof(s1),1,pf);
printf("%s %d %f",s1.a,s1.b,s1.c);
fclose(pf);
pf = NULL;
return 0;
}
因为fwrite是以二进制格式进行写入,故该文件中的值为二进制格式,而不是文本格式
ftell
函数声明:long int ftell ( FILE * stream )
函数介绍:返回文件指针相对于起始位置的偏移量
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
}
//操作文件
for (int i = 0; i < 26; i++)
fputc('a' + i, pf);
printf("%d", ftell(pf));
//关闭文件
fclose(pf);
return 0;
}
fseek
函数声明:int fseek ( FILE * stream, long int offset, int origin )
函数介绍:根据文件指针的位置和偏移量来改变文件指针的位置,最后一个参数只能填三个值(SEEK_SET、SEEK_CUR、SEEK_END分别是文件起始位置、文件指针的当前位置、文件末尾),第二个参数写相对于参数三的偏移量,正数往又偏移,负数往左偏移
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
}
//操作文件
for (int i = 0; i < 26; i++)
fputc('a' + i, pf);
fseek(pf,0,SEEK_SET);
printf("%c",fgetc(pf));//a
fseek(pf,-1,SEEK_END);
printf("%c", fgetc(pf));//z
fseek(pf, -2, SEEK_CUR);
printf("%c", fgetc(pf));//y
//关闭文件
fclose(pf);
return 0;
}
feof用来判断上诉函数是否是读完了文件
ferror用来判断上诉函数是否是读写出现错误
int main()
{
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
}
for (int i = 0; i < 26; i++)
fputc('a' + i, pf);
rewind(pf);
char ch;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c\n",ch);
}
if (feof(pf))
{
perror("feof");
}
else if (ferror(pf))
{
perror("ferror");
}
return 0;
}