【文件】——操作文件
目录
一:文件的定义
所谓文件就是:磁盘上的文件。
在程序设计中,我们主要说的文件一般分为两种:1.程序文件;2.数据文件。
程序文件:字面意思,就是在写程序的过程中所写出的文件。包括源程序文件(后缀为 .c),目标文件(后缀为 .obj),可执行程序文件(后缀为 .exe)等等。
数据文件:文件中的内容不一定是程序,而是程序在运行时所读写的数据,例如程序运行过程中需要从中读取数据的文件,或输出内容的文件。
所谓的操作文件实际上操作的就是 数据文件。
操作数据文件:写程序时,把信息输出到磁盘上,当有需要的时候再从磁盘上将数据读取到内存中使用。即处理磁盘上的文件。
二:文件名
当我们操作某一数据文件时,需要将该文件所处的目录”拿出来“。也就是说一个文件要有一个唯一的文件标识,以便用户们识别和引用。
文件名包含有三部分:文件路径+文件名主干+文件后缀
例如:D:\code\gitee-main\main_-c\2024-5-22文件操作\test.txt
为了方便起见,文件标识也常被称为文件名。
三:文件类型
根据数据的形式,数据文件分为文本文件或者二进制文件。
3.1:二进制文件
二进制文件:数据以二进制的形式存储在文件中。
总的来说,二进制文件中的内容就是我们看不懂的文件。
3.2:文本文件
文本文件:数据在外存上以ASCII码的形式存储的文件。
文本文件就是我们能看的懂的文件。
四:文件的打开与关闭
在学习文件的打开与关闭之前,这必然要先了解学习一下文件指针。
4.1:文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。创建一个 FILE* 的指针变量。
FILE* pf; // 创建一个文件指针变量
定义的pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。
4.2:文件的打开与关闭
程序中是如何对文件进行打开与关闭的呢?
以喝一瓶水为例:第一步,打开水瓶;第二步,喝水;第三步,关闭水瓶。
同理文件操作:第一步,打开文件;第二步,读/写文件;第三步,关闭文件。
所以,文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
打开文件:
关闭文件:
ANSIC规定使用fopen来打开文件,使用fclose来关闭文件。
// 打开文件
FILE* fopen(const char* filename, const char* mode);
//关闭文件
int fclose(FILE* stream)
其中,fopen函数中的 mode 指的是文件的打开方式:
文件使用打开方式 | 含义 | 若指定文件不存在 |
" r "(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 出错 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
FILE* pf = fopen("test.txt", "r"); // 从文件中读取数据
FILE* pf = fopen("test.txt", "w"); // 写数据到文件中
FILE* pf = fopen("test.txt", "a"); // 写数据到文件末尾
FILE* pf = fopen("test.txt", "rb"); // 从二进制文件中读取数据
FILE* pf = fopen("test.txt", "wb"); // 写数据到二进制文件中
先看一个例子代码:
#include<stdio.h>
int main()
{
// 打开文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
return;
}
// 读字符
int ch = 0;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c", ch);
}
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
// 打印结果:hello china
五:文件的顺序读写
顺序读写:在读取遍历文件中的数据时一个接着一个的读取,是有顺序的读写。
读写文件的思维导图 :
5.1:读写字符
读字符:
写字符:
// 读函数使用形式:
int fgetc ( FILE * stream );
// 写函数使用形式:
int fputc ( int character, FILE * stream );
// stream:所操作的文件指针
// character:写入文件的字符
写数据到文件中:
#include<stdio.h>
int main()
{
// 写字符到文件中
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen fail");
return;
}
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fclose(pf);
pf = NULL;
return 0;
}
程序运行前:
程序运行后:
读文件中的数据:
#include<stdio.h>
int main()
{
// 读文件中的数据
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
return;
}
int ch = 0;
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
程序运行结果:
5.2:读写字符串
读写字符串函数使用形式:
// 读字符串
char * fgets ( char * str, int num, FILE * stream );
// str:将从文件中所读的数据放入到一个数组中
// num:从文件中读取num个字符
// stream:所操作的文件指针
// 写字符串
int fputs ( const char * str, FILE * stream );
// str:将str数组中的内容写入到文件中
// stream:所操作的文件指针
写字符串数据
代码实例:
#include<stdio.h>
int main()
{
// 写字符串数据到文件中
char arr[] = "abcdefghi";
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen fail");
return;
}
// 写数据
fputs(arr, pf);
fclose(pf);
pf = NULL;
return0;
}
程序运行前:
程序运行后:
读字符串数据
代码实例:
#include<stdio.h>
int main()
{
// 读文件中的数据
char arr1[30] = { 0 };
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
return;
}
// 读数据
fgets(arr1, 10, pf);
printf("arr1 = %s\n", arr1);
fclose(pf);
pf = NULL;
}
程序运行结果:
5.3:读写格式化数据
// 读格式化数据
int fscanf ( FILE * stream, const char * format, ... );
// 写格式化数据
int fprintf ( FILE * stream, const char * format, ... );
写格式化数据
代码实例:
#include<stdio.h>
// 读写格式化数据
struct Stu
{
char name[20]; // 姓名
int age; // 年龄
char sex[5]; // 性别
};
int main()
{
// 写格式化数据
struct Stu s = { "张三",21,"男" };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen fail");
return;
}
// 写:
fprintf(pf, "%s %d %s", s.name, s.age, s.sex);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
程序运行结果前:
程序运行结果后:
读格式化数据
代码实例:
#include<stdio.h>
// 读写格式化数据
struct Stu
{
char name[20]; // 姓名
int age; // 年龄
char sex[5]; // 性别
};
int main()
{
// 读文件中的数据
struct Stu s1 = { 0 };
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
return;
}
// 读
fscanf(pf, "%s %d %s", s1.name, &(s1.age), s1.sex);
printf("%s %d %s", s1.name, s1.age, s1.sex);
// 关闭文件
fclose(pf);
pf = NULL;
}
运行结果:
六:文件的随机读写
随机读写:读取文件信息时并不是一个接着一个来读取的,是随机的,不固定的。
文件的随机读写有三个函数来操作:
// 1.fseek
// 根据文件指针的位置和偏移量来定位文件指针
int fseek ( FILE * stream, long int offset, int origin );
// offset:偏移量
// origin:相对位置
// 2.ftell
// 返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
// 3.rewind
// 让文件指针的位置回到文件的起始位置
void rewind ( FILE * stream );
6.1:fseek
功能:根据文件指针的位置和偏移量来定位文件指针
int fseek ( FILE * stream, long int offset, int origin );
// offset:偏移量
// origin:相对位置
6.2:ftell
功能:返回文件指针相对于起始位置的偏移量
当不知道文件指针在哪个位置时,可以通过 ftell 函数来知道该指针的位置。
6.3:rewind
功能:让文件指针的位置回到文件的起始位置
七:文件读取结束的判定
我们在在学习的过程中,可能会遇到一些函数来判断文件是否结束,例如:feof函数。
但是,在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候, 判断是读取失败结束,还是遇到文件尾结束。文本文件读取是否结束, 判断返回值是否为EOF (fgetc),或者NULL(fgets)例如:
- fgetc判断是否为EOF.
- fgets判断返回值是否为NULL