【文件指针+文件顺序读写操作函数】
1.文件的打开和关闭
- 1.1 什么是文件指针
2.文件操作函数
- 2.1 fgetc函数和fputc函数
- 2.2 fgets函数和fputs函数
- 2.3 fscanf函数和fprintf函数
- 2.4 fwrite函数和fread函数
1.文件的打开和关闭
1.1 什么是文件指针?
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名
字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统
声明的,取名FILE.
假如:我们要操作一个文件,名为text.txt ,首先要打开文件,打开文件的同时,操作系统会自动为该文件创建一个文件信息区,专门用来记录该文件的信息。
文件信息区的每一个信息与text.txt的信息是一一对应的。而该文件信息区名为struct _iobuf,又被重命名为 FILE 。
所以FILE其实就是文件信息区。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下面我们可以创建一个FILE*的指针变量:
FILE* pf; 文件指针变量
通过该文件指针变量,我们就可以读写文件中的信息。
文件在读写之前,需要打开文件
//打开文件
FILE * fopen ( const char * filename, const char * mode );
读写文件完成后,需要关闭文件:
//关闭文件
int fclose ( FILE * stream );
文件的打开方式如下:
举个例子:
#include <stdio.h>
int main ()
{
//打开文件
FILE* pFile = fopen ("text.txt","w");
//文件操作
if (pFile!=NULL)
{
fputs ("fopen",pFile);
//关闭文件
fclose (pFile);
}
return 0;
}
该段代码的意思是:打开一个文件叫text.txt,以写的形式打开。意味着向文件中写入信息。具体是怎么写的,下面会讲到。
写完信息后关闭文件,fclose(pFile),pFile就是一个文件指针。
文件指针就是用来操作文件的,假如我们需要对文件进行写入的操作,就使用文件指针打开该文件并定义"写"的操作。
2.文件操作函数
2.1 fgetc函数和fputc函数
fgetc函数和fputc函数是针对字符的输入输出的。
int fgetc(FILE* stream);
//从流中读取字符,返回读到的ascii码值
流是什么呢?可以把流理解成水流,水流到尽头,就是一个蓄水池。一个蓄水池就相当于一个存储大量文件的区域。文件也类似,从流中(文件信息区)中读取文件,fgetc返回读到的ascii码值。
int fputc(int character, FILE* stream);
//把字符character 写入流中
fputc是将一个字符写入文件中。一次写入一个字符,返回成功写入的字符的个数。
举个例子:
int main()
{
FILE* pf =fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//写入字符
char ch = 0;
for (ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch, pf);
}
fclose(pf);
pf = NULL;
return 0;
}
先打开text.txt文件,如果不存在该文件,则会新建一个文件("w"写入的形式会新建一个文件,但是以"r"的形式打开文件,如果文件不存在,会读取文件失败,返回NULL)
然后向文件中写入a~z个字母。
运行成功,成功写入文件:
写入成功,现在向文件中读取数据
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//写入字符
char ch = 0;
for (ch = 'a'; ch <= 'z'; ch++)
{
int ch = fgetc(pf);
printf("%c ", ch);
}
fclose(pf);
pf = NULL;
return 0;
}
读取成功,如下图:
总结:fgetc函数是向文件指针pf(或者其他名字,由你来定)一次读取一个字符,读取完第一个字符后,指针自动跳到下一个字符。
fputc函数是向pf指向的指针一次写入一个字符。
2.2 fgets函数和fputs函数
int fputs(const char* str, FILE* stream);
向流中写入一行数据,一次性写一行
举个例子:
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 0;
}
fputs("Hello World","w");
fclose(pf);
pf = NULL;
return 0;
}
写入成功。
注意:打开文件进行写入操作时,上一次写入的数据将会被清除。
接下来向从文件中读取数据:
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//读文件,一次读一行
char buf[20];
fgets(buf, 10, pf); // 读10个字节,相当于读9个,最后一个字节留着放\0
printf("%s\n", buf);
fclose(pf);
pf = NULL;
return 0;
}
注意:当我们读取10个字节时,实际上打印出来只打印前面9个字节的内容,还有一个字节是被用来留着放\0的。
还有一种情况,假如我们需要读取20个字节的数据,然而第一行不足20个字节,fgets读完所有的数据后,即使不够20个字节,就不会再读取了,就停止了。不会跳到第二行继续读。 更说明fges是一次只读取一行。
2.3 fscanf函数和fprintf函数
前面说过,fscanf函数和fprintf函数是格式化输入输出函数。什么是格式化函数呢?
其实格式化函数就是对于不同格式的数据都能够进行输入输出,比如:整型,浮点型,结构体类型等等,这些就是不同格式的数据。
相较于scanf和printf函数,fscanf函数和fprintf只是多了一个参数,即FILE指针所指向的文件。fscanf函数是向FILE的指针指向的文件中读取格式化的数据,fprintf函数是向FILE*的指针指向的文件中写入格式化的数据。
对比如下:
int fscanf ( FILE * stream, const char * format, ... );
int scanf ( const char * format, ... );
int fprintf ( FILE * stream, const char * format, ... );
int printf ( const char * format, ... );
举一个简单的例子:
向text.txt文件中写入结构体数据
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 0;
}
struct S
{
char name[20];
int age;
float score;
}s = {"zhangsan",20,99.5f};
fprintf(pf,"%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
运行成功后,文件中就有了该结构体的数据。
对于fscanf来说亦是如此:
以读的方式打开该文件,对其中的数据进行读取。读取后,打印出来看即可。
s.name不用&的原因是,s.name是一个数组名,表示首元素地址,不需要&,而其他的age和score则需要&。
前面说过,这几个函数都是适用于所有输入流输出流,那么就包括了键盘(标准输入流),屏幕(标准输出流)。
int main()
{
int ch = fgetc(stdin);
fputc(ch, stdout);
}
此时,我们会从键盘中读取一个字符,写入到屏幕中。
对于其他函数也是如此。仍可以从键盘中读取数据,写入到屏幕上。
2.4 fwrite函数和fread函数
上面提到,fwrite函数和fread函数是以二进制的形式进行写入和读取的。
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
先来看看fwrite在库中的声明。
fwrite函数是向stream这个流中一次写count个大小为size的ptr指向的内容。
看不懂没关系,举个例子:
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { "zhangsan",20,95.5f };
FILE* pf = fopen("text.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 0;
}
fwrite(&s, sizeof(S), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
此时我们创建了一个结构体,该结构体有三个成员,我们以二进制写入的形式打开文件进行写入,此时向pf指向的文件中写入s这块空间,一次写入1个sizeof(S)大小的内容。
运行成功后,打开text.txt文件:
你会发现出了拼音zhangsan,其他的都看不懂,没关系,因为这是二进制形式。
看不懂并不代表代码是错误的。
既然我们看不懂,我们就让编译器来看,下面我们以二进制读取的方式,向文件中读取数据出来。
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
既然是读取,那么就是跟写入相反而已。
从stream流指向的文件中读取ptr这块空间的内容,一次读取count个大小为size个字节的内容。
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { 0 };
FILE* pf = fopen("text.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 0;
}
fread(&s, sizeof(S), 1, pf);
printf("%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
此时我们向pf流指向的文件中读取s这块空间的内容,一次读取1个大小为sizeof(S)个字节的内容。读取成功后,我们就打印出来看看。
打印结果确实符合预期。
总结:fwrite 和fread函数是向文件中以二进制的形式写入和读取文件的。