在C语言学习中,文件操作必不可少,比如我们之前制作的扫雷小游戏,假如玩家玩到一半的时候突然有些事情需要去处理,但是他想在处理完事情后继续玩下去,这时候怎么办呢?这时候我们便可以将数据储存进文件中,而下次打开游戏时,我们再将上次储存进文件中的数据再次加载进程序中即可。生活中,我们许多程序都需要用到文件,这篇文将带着大家一起学习关于文件的基本知识以及一些文件函数的使用。
目录
3.4、printf、fprintf、sprintf与scanf、fscanf、sscanf的区别
一、文件的基本知识
相信文件这个概念相比大家都很熟悉,每个人电脑上都有许多大大小小的文件,当然文件也会有分类。
1、程序文件与数据文件
程序文件
比如我们创建的源文件(.c)以及目标文件,可执行程序等等我们都统称为程序文件。
数据文件
所谓数据文件,则是保存数据的文件,在程序运行过程中,程序可以访问这些数据文件,拿到或写入他们想读/写的数据。
2、文件名
文件名想必大家都有所了解,但是一个完整的文件名其实是由文件路径+文件标识+文件后缀组成的。
例如:我在C盘符中的code文件夹储存了一个叫test.c的文件,这时,这个文件完整的文件名应该是:c:\code\test.c
二、文件相关函数
我们想要对文件进行操作,首先我们必须的了解文件指针的概念;
1、文件指针
每个文件在被使用前,系统都会创建一块内存区域来保存这个文件的相关信息,比如文件当前状态,文件名,文件当前位置等等;这些文件信息都被保存在了一个结构体变量中,并且该结构体类型被重定义为FILE。当我们需要对文件进行操作时,就需要通过这些文件信息了;我们可以通过一个指针来指向这个结构体变量,以达到对文件进行操作,而我们打开一个文件时,系统会自动创建文件信息区,并返回一个指向该文件信息区的指针 FILE*;我们此时便可用一个FIEL*类型指针来接收;
FILE* pf;
2、文件的打开与关闭
当我们想在冰箱里取出食物时,我们得打开冰箱,然后取食物,最后还得关上冰箱;文件操作也是如此,我们想对文件进行操作时,我们必须得先打开文件,然后对文件进行操作,最后对文件操作完毕后我们必须关上文件。具体操作如下;
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");
//对文件进行操作
//....
//关闭文件
fclose(pf);
return 0;
}
如图fopen第一个参数为文件名,第二个参数为打开方式;
文件使用方式 | 含义 | 如果指定文件不存在 |
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 报错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 报错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 报错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 报错 |
“w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 报错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
3、文件的顺序读写
3.1、fgetc和fputc函数
这两个函数都是一个字符一个字符得操作文件;
int main()
{
//以写得方式打开文件
FILE* pfin = fopen("test.txt", "w");
if (pfin == NULL)
{
perror("fopen fail:");
exit(-1);
}
//写入文件
char start = 'A';
for (int i = 0; i < 26; i++)
{
fputc(start + i, pfin);
}
//关闭文件
fclose(pfin);
pfin = NULL;
//以读的方式打开文件
FILE* pfout = fopen("test.txt", "r");
if (pfout == NULL)
{
perror("fopen fail:");
exit(-1);
}
//读取文件
char ch;
//EOF为文件结束的标志(与‘\0’类似)
while ((ch = fgetc(pfout)) != EOF)
{
printf("%c ", ch);
}
//关闭文件
fclose(pfout);
pfout = NULL;
return 0;
}
注:读文件和写文件时,读/写一个字符后,指针会自动跳到下一个字符那,无需使用者挪动指针。
3.2、fgets和fputs函数
这两个函数与gets和puts类似,只不过fgets和fputs可以从任何流输入/输出,而gets只能从标准输入流(键盘)获取数据,gets只能从标准输出流(屏幕)输出数据。
参数:
参数一:char* string
该参数是读取数据存得位置,可理解从流中读取数据,存入该参数中;
参数二:int n
该参数最多读取字符个数,实际读取数据为n-1;
参数三:FILE* stream
数据流,读取数据得来源;
返回值:
返回读取字符串; 若未读取到字符串或读取失败则返回NULL
参数:
参数一:const char* string
想要写入的字符串;
参数二:FILE* stream
写入的位置;
返回值:int
该函数会返回读取字符串的个数;
观看以下代码,了解这两个函数的用法;
int main()
{
//以写的方式打开文件
FILE* pfin = fopen("test.txt", "w");
if (pfin == NULL)
{
perror("fopen fail:");
exit(-1);
}
//写入
fputs("hello C语言!\n", pfin);
fputs("你好,文件指针\n", pfin);
//关闭文件
fclose(pfin);
pfin = NULL;
//以读的方式打开文件
FILE* pfout = fopen("test.txt", "r");
if (pfout == NULL)
{
perror("fopen fail:");
exit(-1);
}
//一行一行读文件
char str[20];
fgets(str, 15, pfout);
printf("%s\n", str);
fgets(str, 15, pfout);
printf("%s\n", str);
//关闭文件
fclose(pfout);
pfout = NULL;
return 0;
}
3.3、fscanf和fprintf函数
这两个函数为任意流的输入输出函数,而printf和scanf为标准输入输出流函数。
其用法除了在最前面加个流参数与printf和scanf无异;
//将结构体里的数据写入文件
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s1 = { "zhangsan", 20, 85.5f };
//打开文件
FILE* pfin = fopen("test.txt", "w");
if (pfin == NULL)
{
perror("fopen fail");
exit(-1);
}
//将结构体数据写入文件中
fprintf(pfin, "%s %d %.2f", s1.name, s1.age, s1.score);
//关闭文件
fclose(pfin);
pfin = NULL;
return 0;
}
//将数据读入结构体中
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s1 = { 0 };
//打开文件
FILE* pfout = fopen("test.txt", "r");
if (pfout == NULL)
{
perror("fopen fail");
exit(-1);
}
//将数据读入结构体
fscanf(pfout, "%s %d %f", s1.name, &(s1.age), &(s1.score));
printf("%s %d %f\n", s1.name, s1.age, s1.score);
//关闭文件
fclose(pfout);
pfout = NULL;
return 0;
}
3.4、printf、fprintf、sprintf与scanf、fscanf、sscanf的区别
printf 和 scanf 系列
printf:按照一定的格式向屏幕输出数据
scanf:按照一定的格式从键盘输入数据
fprintf 和 fscanf 系列
fprintf: 按照一定的格式从输入流(stdin/文件)输出(写)数据
fscanf:按照一定的格式从输出流(stdout/文件)输入(读)数据
sprintf 和 sscanf 系列
sprintf:把格式化的数据按照一定的格式转换为字符串
sscanf:从字符串中按照一定的格式读取格式化的数据
4、随机读写
4.1、fseek函数
将文件指针移动到特定的位置
int fseek ( FILE * stream, long int offset, int origin );
其中第三个参数有三个选择:如下
SEEK_SET为从文件开始的位置
SEEK_CUR为从当前位置
SEEK_END为从文件末尾的位置
下面,我们分别用这三个参数来读取文件
int main()
{
//文件内容
//abcdefg
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
exit(-1);
}
char ch = fgetc(pf);
//将文件指针偏移到开始位置往后一个的位置
fseek(pf, 1, SEEK_SET);
printf("%c\n", ch);
//将文件指针偏移到当前位置的后3个的位置
fseek(pf, 3, SEEK_CUR);
ch = fgetc(pf);
printf("%c\n", ch);
//将文件指针偏移到文件末尾位置往前3个的位置
fseek(pf, -3, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
4.2、ftell函数
该函数能告诉我们文件指针当前对应起始位置的偏移量
long ftell( FILE *stream );
int main()
{
//文件内容
//abcdefg
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
exit(-1);
}
//将指针从起始位置开始往后偏移3
fseek(pf, 4, SEEK_SET);
int ret = ftell(pf);
printf("%d\n", ret);
fclose(pf);
pf = NULL;
return 0;
}
4.3、rewind函数
该函数用于将文件指针初始化为最开始的位置
void rewind( FILE *stream );
int main()
{
//文件内容
//abcdefg
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
exit(-1);
}
//将指针从起始位置开始往后偏移3
fseek(pf, 4, SEEK_SET);
int position = ftell(pf);
printf("%d\n", position);
//初始化指针位置
rewind(pf);
position = ftell(pf);
printf("%d\n", position);
fclose(pf);
pf = NULL;
return 0;
}
文件的基本操作就是以上几种了,当然还有更多文件相关函数本文并未提及,如果大家有更多的需求,可以通过官方文档学习那些文件相关函数(C语言手册)。