1. 什么是文件
磁盘上的文件是文件。
但是在程序设计中,一般谈的文件有两种:程序文件和数据文件。
1.1 程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
1.2 数据文件
文件内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或是输出数据的文件。
1.3 文件名
一个文件要有一个唯一的标识,方便用户的识别和引用。
文件名包含三部分:文件路径 + 文件名主干 + 文件名后缀
比如:D:\SteamLibrary\steam.dll
文件标识也常被称为文件名。
2. 文件的打开和关闭
打开方式 | 含义 | 指定文件不存在时 |
“r”(只读) | 打开一个存在的文本文件 | 出错 |
“w”(只写) | 打开一个文本文件 | 建立新文件 |
“a”(追加) | 向文本文件末尾添加数据 | 建立新文件 |
“rb”(只读) | 打开一个二进制文件 | 出错 |
“wb”(只写) | 打开一个二进制文件 | 建立新文件 |
“ab”(追加) | 向二进制文件末尾添加数据 | 建立新文件 |
“r+“(读写) | 打开一个文本文件 | 出错 |
“w+”(读写) | 新建一个文本文件 | 建立新文件 |
“a+”(读写) | 打开一个文件,在文件末尾进行读写 | 建立新文件 |
“rb+“(读写) | 打开一个二进制文件 | 出错 |
“wb+“(读写) | 新建一个二进制文件 | 建立新文件 |
“ab+”(读写) | 打开二进制文件,在文件末尾进行读写 | 建立新文件 |
2.1 文件指针
缓冲文件系统中,关键的概念是"文件类型指针",简称"文件指针"。
每个被使用的文件都在内存中开辟了对应一个文件信息区,用来存放文件的相关信息(如文件名字,文件状态,文件所在位置)。这些信息存放在一个结构体变量中,这个结构体类型由系统声明,取名为FILE。
VS2022编译环境提供的 stdio.h 头文件有以下申明:
不同C语言编译器下FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件,系统会根据文件状态自动创建一个FILE结构的变量,并填充其中的信息。
一般都是创建一个文件指针来维护这个FILE结构的变量,这样更加方便。
下面是一个文件指针的创建:
FILE* pf;
定义 pf 是一个指向FILE类型的指针类型,可以使 pf 指向某个文件的文件信息区。通过该文件信息区中的信息就可以访问该文件,通过文件指针变量就可以找到与它关联的文件。
2.2 文件的打开和关闭
文件在读写之前应该先打开文件,使用完毕后应关闭文件。
ANSIC 规定打开文件使用 fopen 函数,关闭文件使用 fclose 函数。
在编写程序时,打开文件的同时,会返回一个FILE* 的指针变量指向该文件,相当于建立了文件和指针的关联。
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
打开方式如下:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
return 0;
}
3. 文件的顺序读写
3.1 顺序读写函数介绍
fgetc 和 fputc
int fputc ( int character, FILE * stream );输出一个字符到 stream 中。
int fgetc ( FILE * stream );从 stream 中输入一个字符。
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
return 0;
int i = 0;
for (i = 0; i < 26; i++)
{
fputc('a' + i, pf); //将26个英文字母按顺序输出到文件
}
fclose(pf);
pf = NULL;
return 0;
}
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
return 0;
int i = 0;
char ch = 0;
for (i = 0; i < 26; i++)
{
ch = fgetc(pf);
printf("%c\n", ch); //按顺序输入数据到屏幕
}
fclose(pf);
pf = NULL;
return 0;
}
fgets 和 fputs
int fputs ( const char * str, FILE * stream );输出一个字符串到 stream 中。
char * fgets ( char * str, int num, FILE * stream );从 stream 中输入(num-1)个字符到 str 所指向的位置。
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
return 0;
fputs("hello world", pf); //输出字符串到文件
fclose(pf);
pf = NULL;
return 0;
}
int main()
{
char s[10];
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
return 0;
fgets(s, 10, pf); //将文件中的数据输入到s中存储
printf("%s\n", s);
fclose(pf);
pf = NULL;
return 0;
}
fscanf 和 fprintf
int fprintf ( FILE * stream, const char * format, ... )将要输出的数据按照 printf 的格式输出到 stream。
int fscanf ( FILE * stream, const char * format, ... );按照 scanf 的格式从 stream 中输入数据。
int main()
{
int a = 100;
float f = 3.14f;
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
return 0;
fprintf(pf, "%d %.2f", a, f); //按照格式将数据输出到pf
fclose(pf);
pf = NULL;
return 0;
}
int main()
{
int a = 0;
float f = 0.0f;
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
return 0;
fscanf(pf, "%d %f", &a, &f); //按照格式读取文件中的数据
printf("%d %f", a, f);
fclose(pf);
pf = NULL;
return 0;
}
fread 和 fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );找到 str 指向的数据,输出 count 个 size 大小的数据到 stream。
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );从 stream 输入 count 个 size 大小的数据到 str 指向的地址。
struct S
{
int a;
float f;
char arr[10];
};
int main()
{
struct S s = { 100, 3.14f, "hello" };
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
return 0;
fwrite(&s, sizeof(struct S), 1, pf);
//s指向的地址,找到1个sizeof(struct S)大小的数据,输出到pf
fclose(pf);
pf = NULL;
return 0;
}
int main()
{
struct S s = {0};
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
return 0;
fread(&s, sizeof(struct S), 1, pf);
printf("%d %f %s", s.a, s.f, s.arr);
//从文件pf中取出1个sizeof(struct S)大小的数据,存放到s的地址
fclose(pf);
pf = NULL;
return 0;
}
4. 文件的随机读写
4.1 fseek
int fseek ( FILE * stream, long int offset, int origin );根据文件指针的位置和偏移量来定位文件指针。
origin的三个参数:
SEEK_SET | 文件开始 |
SEEK_CUR | 文件当前位置 |
SEEK_END | 文件末尾 |
//此处文本中是26个按顺序排放的英文字母
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
return 0;
fseek(pf, 5, SEEK_SET); //将文本指针重新定位
char ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
4.2 ftell
long int ftell ( FILE * stream );返回文件指针相对于起始位置的偏移量。
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
return 0;
fseek(pf, -10, SEEK_END);
printf("%d\n", ftell(pf)); //修改文本指针位置后再打印距离起始位置的偏移量
fclose(pf);
pf = NULL;
return 0;
}
4.3 rewind
void rewind ( FILE * stream );将文本指针的位置重置到起始位置。
5. 文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或二进制文件。
数据在内存以二进制形式存储,不加转换输出到外存,就是二进制文件。
如果要求在外存以ASCII形式存储,则需要在存储前进行转换,以ASCII形式存储的文件就是文本文件。
字符一律使用ASCII形式存储,数值型数据既可以使用ASCII形式存储,也可以使用二进制形式存储。
下面是10000在内存中两种存储形式: