目录
- 程序文件
- 数据文件
一,文件分类
程序文件
- 源程序文件(.c);
- 目标文件(windows环境.obj),编译过程中的临时文件;
- 可执行文件(windows环境.exe)等;
数据文件
- 文件的内容不一定是程序,而是程序运行时读写的数据;
- 如程序运行需要从中读取数据的文件,或输出内容的文件;
二,文件
- 一个文件要有一个唯一的文件标识,以便用户识别和引用;
文件名
- 文件路径
- 文件名主干
- 文件后缀
如c:\code\test.txt
文件指针
- 在缓冲文件系统中,关键的概念是“文件类型指针”,简称文件指针;
- 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息;
- 如文件的名字,文件状态及文件当前的位置等;
- 这些信息是保存在一个结构体变量中的,该结构体类型系统有声明,取名FILE;
- 不同C编译器,FILE类型包含的内容不完全相同,但大同小异;
- 每当打开一个文件时,系统会根据文件情况,自动创建一个FILE结构的变量,并填充其中的信息;
- 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便;
- 如 FILE* pf;
三,文件的打开和关闭
- 文件在读写之前,应该先打开文件,在使用结束后,应该关闭文件;
- 打开文件的同时,都会返回一个FILE*的指针变量指向该文件;
- 打开文件fopen,关闭文件fclose;
int main()
{
FILE* pf = fopen("F:\\VS\\Project1\\test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 0;
}
fclose(pf);
pf = NULL;
return 1;
}
注:文件打开方式
四,流概念
- 文件流;
- 标准输入输出流stdin/stdout;
注:默认打开3个流(类型均为FILE*)
- stdin,标准输入流(键盘);
- stdout,标准输出流(屏幕);
- stderr,标准错误流(屏幕);
fgetc(stdin); //从流(屏幕)中,读取一个字符;
fputc('b', stdout); //输出/写入一个字符,到流(屏幕)中;
标准输入输出流读写
- gets,获取一行字符串从stdio;
- puts,写出一行字符串到stdout;
char* ch[10];
gets(ch); //从stdin流中,获取一行;
puts(ch); //输出/写入字符串,到stdout流中;
备注:
- scanf/printf,针对标准输入输出流,格式化输入输出语句;
- fscanf/fprintf,针对所有输入输出流,格式化输入输出语句;
- sscanf/sprintf,sscanf是从字符串中读取格式化的数据,sprintf是把格式化数据输出到字符串;
四,文件读写
文件的顺序读写
内存与硬盘读写:
文件读写函数:![]()
//输入、输出当个字符
int main()
{
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//输出/写入字符
fputc('b', pf);
fputc('i', pf);
fputc('t', pf);
//文件指针移动到起始位置
fseek(pf, 0L, SEEK_SET);
//输入/读取字符
printf("%c", fgetc(pf));
printf("%c", fgetc(pf));
printf("%c", fgetc(pf));
fclose(pf);
pf = NULL;
return 1;
}
//输入、输出一行字符
int main()
{
char str[] = "abcde";
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//输出/写入字符串
fputs("bit", pf);
//文件指针移动到起始位置
fseek(pf, 0L, SEEK_SET);
//输入/读取字符串
fgets(str, 2, pf); //2个字符包含'\0'
printf("%s", str);
fgets(str, 2, pf); //2个字符包含'\0'
printf("%s", str);
fclose(pf);
pf = NULL;
return 1;
}
//格式化数据输入、输出
struct S
{
char str[100];
int num;
};
int main()
{
struct S s1 = { "bit",10 };
struct S s2 = { {0},0 };
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//输出/写入格式化数据
fprintf(pf, "%s ", s1.str);
fprintf(pf, "%d ", s1.num);
//文件指针移动到起始位置
fseek(pf, 0L, SEEK_SET);
//输入/读取格式化数据,截止到空格
fscanf(pf, "%s", s2.str);
fscanf(pf, "%d", &(s2.num));
printf("%s %d", s2.str, s2.num);
fclose(pf);
pf = NULL;
return 1;
}
//二进制输入、输出
struct S
{
char str[100];
int num;
};
int main()
{
struct S s1 = { "bit",10 };
struct S s2 = { {0},0 };
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//二进制输出/写入
fwrite(&s1,sizeof(struct S),1,pf);
//文件指针移动到起始位置
fseek(pf, 0L, SEEK_SET);
//二进制输入/读取
fread(&s2, sizeof(struct S), 1, pf);
printf("%s %d", s2.str, s2.num);
fclose(pf);
pf = NULL;
return 1;
}
struct S
{
char name[10];
int num;
float score;
};
int main()
{
struct S s1 = { "lisi",20,99.5f };
char str[100] = { 0 };
//从s1中格式化数据,输出到str
sprintf(str, "%s %d %f", s1.name, s1.num, s1.score);
printf("%s\n", str);
struct S s2 = { 0 };
//从str中读取格式化数据,到s2
sscanf(str, "%s %d %f", s2.name, &(s2.num), &(s2.score));
printf("%s %d %f", s2.name, s2.num, s2.score);
return 0;
}
文件的随机读写
- fseek,根据文件指针的位置和偏移量来定位文件指针;
- origin,需为SEEK_CUR(当前位置)、SEEK_END(文件末尾位置)、SEEK_SET(文件起始位置);
- ftell,返回文件指针相对于起始位置的偏移量;
- rewind,让文件指针的位置回到文件的起始位置;
int main()
{
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 0;
}
//输出/写入字符
fputc('b', pf);
fputc('i', pf);
fputc('t', pf);
//文件指针移动到起始位置
fseek(pf, 0L, SEEK_SET);
printf("%c\n", fgetc(pf));
//文件指针移动到末尾位置
fseek(pf, -2L, SEEK_END);
printf("%c\n", fgetc(pf));
//文件指针移动到当前位置
fseek(pf, 0L, SEEK_CUR);
printf("%c\n", fgetc(pf));
//返回当前指针偏移量
printf("%d\n", ftell(pf));
//文件指针移动到起始位置
rewind(pf);
printf("%c\n", fgetc(pf));
fclose(pf);
pf = NULL;
return 1;
}
五,其他
文本文件和二进制文件
- 数据的组织形式可分为文本文件和二进制文件;
- 数据在内存中以二进制形式存储,不加转换即输出到外存的,即为二进制文件;
- 数据在内存中以二进制形式存储,转换为ASCII码输出到外存的,即为文本文件;
数据在文件中存储形式
- 字符一律以ASCII码形式存储;
- 数值型数据,即可ASCII码形式存储,也可二进制形式存储;
文件的结束判定
feof
- 常被错误使用,用来判定文件结束;
- 在文件读取过程中,不能用feof函数的返回值,来直接判断文件是否结束;
- 而是应该用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束;
文本文件读取是否结束
- fgetc,读取结束时返回EOF,正常读取为字符ASCII码值;
- fgets,读取结束时返回NULL,正常读取为字符串起始位置;
二进制文件读取是否结束
- fread,正常返回实际元素个数,如小于实际要读的个数则文件读取结束了;
//将文件test1.txt,复制到test2.txt
int main()
{
FILE* pfr = fopen("test1.txt", "r");
if (pfr == NULL)
{
perror("pfr");
return 0;
}
FILE* pfw = fopen("test2.txt", "w");
if (pfw == NULL)
{
perror("pfw");
return 0;
}
char c = 0;
//读取失败或文件结束时,均会返回EOF
while ((c = fgetc(pfr)) != EOF)
{
fputc(c, pfw);
}
if (ferror(pfr))
puts("I/0,error when reading!");
else if (feof(pfr))
puts("End of File!");
fclose(pfr);
pfr = NULL;
fclose(pfw);
pfw = NULL;
return 1;
}
文件缓冲区
- ANSIC标准采用“缓冲文件系统”处理数据文件的;
- 缓冲文件系统,是指系统自动地在内存中为程序中每个正在使用的文件开辟一块“文件缓冲区”;
- 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上;
- 从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区,充满缓冲区后在逐个地将数据送到程序数据区;
- 缓冲区的大小根据C编译器系统决定的;
#include <stdio.h>
#include <Windows.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("pf");
return 0;
}
//将代码写入输出缓存区
fputc('a', pf);
//睡眠10s,文件内并未写入内容
Sleep(10000);
//刷新缓存区
fflush(pf);
//睡眠10s,文件内已写入内容
Sleep(10000);
//关闭文件也会刷新缓存区
fclose(pf);
pf = NULL;
return 1;
}
三,案例