1.为什么使用文件?
如果没有文件,我们写的程序的数据是存储在电脑的内存中的,如果程序退出,内存回收,数据就没了,当再次运行程序时,是没有上一次的数据的,如果想要把数据持久保存,我们可以使用文件。
2.什么是文件?
磁盘上的文件就是文件
在程序设计中,我们一般把文件分成两种:程序文件,数据文件(从功能角度来分类的)。
- 程序文件:程序文件包括源程序文件(后缀 .c),目标文件( .obj),可执行程序(windows环境后缀为.exe)
- 数据文件:文件的内容不一定是程序,而是程序运行时写的数据。
文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用
文件名包含三部分:文件路径+文件名主干+文件后缀
文件标识常被称为文件名
3.二进制文件和文本文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
一个数据在内存中的存储:
- 字符一律以ASCII形式存储
数值型数据既可以用ASCII形式也可以使用二进制形式存储
4.文件的打开和关闭
1.流和标准流
流:我们的程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对文件,画面,键盘等数据的输入输出操作都是通过流操作的
一般情况下,我们要想在流里写数据,或者从流中读取数据,都是要打开流,然后操作
标准流:
我们在从键盘输入输出数据,向屏幕上输出数据,并没有打开流
因为C程序启动时,默认打开了三个流:
- stdin - 标准输入流,在大多数的环境中从键盘输入(scanf)
- stdout - 标准输出流,大多数的环境中输出至显示器界面(printf)
- stderr - 标准错误流,大多数环境输出到显示器界面
这是默认打开了这三个流
这三个流的类型是:FILE* ,通常称为文件指针
2.文件指针
缓存文件系统中,关键的概念是“ 文件类型指针 ”——“ 文件指针 ”
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息
这些信息存放在结构体中,该结构体类型是由系统声明的,取名FILE
通过文件指针变量能够间接找到与它相关联的文件
3.文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束后应该关闭文件。
打开文件同时,都会返回一个指针变量指向该文件,相当于建立了指针与文件的关系
fopen--打开文件 fclose--关闭文件
FILE* fopen(const char* filename,const char* mode);
int fclose(FILE* filename);
mode表示打开模式
5.文件的顺序读写
顺序读写函数介绍
1.fgetc
字符输入函数 适用与所有输入流
#include<stdio.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%d ", ch);
ch = fgetc(pf);
printf("%d ", ch);
ch = fgetc(pf);
printf("%d ", ch);
fclose(pf);
pf = NULL;
return 0;
}
2.fputc
字符输出函数 适用于所有输入流
#include<stdio.h>
int main()
{
FILE* pfread = fopen("text.txt", "r");
if (pfread == NULL)
{
perror("fopen->text");
return 1;
}
FILE* pfwrite = fopen("date.txt", "w");
if (pfwrite == NULL)
{
fclose(pfread);
pfread = NULL;
perror("fopen->date");
return 1;
}
int ch = 0;
while ((ch=fgetc(pfread)) != -1)
{
fputc(ch, pfwrite);
}
fclose(pfread);
fclose(pfwrite);
pfread = NULL;
pfwrite = NULL;
return 0;
}
3.fgets
文本行输入函数 适用于所有输入流
#include<stdio.h>
int main()
{
FILE* pf = fopen("date.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[20] = "xxxxxxxxxxxxxxxx";
fgets(arr, 10, pf);
fclose(pf);
printf(arr);
pf = NULL;
return 0;
}
4.fputs
文本行输出函数 适用于所有输出流
#include<stdio.h>
int main()
{
FILE* pf = fopen("date.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("abcdef\n", pf);
fputs("abcdef\n", pf);
fputs("abcdef\n", pf);
fclose(pf);
pf = NULL;
return 0;
}
5.fscanf
格式化输入函数 适用于所有输入流
#include<stdio.h>
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s = {0};
FILE* pf = fopen("date.txt", "r");
if (pf == NULL)
{
return 1;
}
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score)); //可变参数列表
fprintf(stdout, "%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
6.fprintf
格式化输出函数 所有输出流
#include<stdio.h>
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s = { "cjx",18,99.5f };
FILE* pf = fopen("date.txt", "w");
if (pf == NULL)
{
return 1;
}
fprintf(pf, "%s %d %f", s.name, s.age, s.score); //可变参数列表
fclose(pf);
pf = NULL;
return 0;
}
7.fread / fwrite
二进制输入输出 适用于文件
fwrite/fread 二进制
#include<stdio.h>
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s = {"cjx",18,100.0f};
FILE* pf = fopen("date.txt", "wb"); //wb
if (pf == NULL)
{
return 1;
}
//fwrite(&s, sizeof(s), 1, pf);//二进制形式
//&s为被写的起始地址
//sizeof(s)写的元素大小
//1 为写几个元素
//pf--写的地方
fread(&s, sizeof(s), 1, pf);
//&s为将读到的数据放的地方的起始地址
printf("%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
6.文件的随机读写
1.fseek
根据文件指针的位置和偏移量来定义文件指针
int fseek(FILE* stream, long int offset,int origin);
2.ftell
返回文件指针相对于起始位置的偏移量
long int ftell(FILE* stream);
3.rewind
让文件指针的位置回到文件的起始位置
void rewind(FILE* stream);
fseek/ftell/rewind
//#include<stdio.h>
//int main()
//{
// FILE* pf = fopen("date.txt", "r");
// if (pf == NULL)
// {
// return 1;
// }
//
// int ch = fgetc(pf);
// printf("%c ", ch);
//
// ch = fgetc(pf);
// printf("%c ", ch);
//
// ch = fgetc(pf);
// printf("%c ", ch);
//
// ch = fgetc(pf);
// printf("%c ", ch);
//
// ch = fgetc(pf);
// printf("%c ", ch);
//
// ch = fgetc(pf);
// printf("%c ", ch);
// int a = ftell(pf); //返回此时文件指针所在位置
// printf("%d ", a);
//
// fseek(pf, -4, SEEK_CUR);//指当前位置
// ch = fgetc(pf);
// printf("%c ", ch);
//
// rewind(pf);//文件指针回到起始位置
// ch = fgetc(pf);
// printf("%c ", ch);
//
// fclose(pf);
// pf = NULL;
// return 0;
//}
7.文件读取结束的判定
foef的作用是:当文件读取结束的时候,判断是读取结束的原因是否是——遇到文件尾结束
- 文本文件读取是否结束,判断返回值是否为 EOF (fgetc),或者NULL(fgets)
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
8.文件缓存区
从内存向磁盘输出数据会先送到内存中的缓存区,装满缓存区后才一起送到磁盘上。
因为有缓存区的存在,C语言在操作文件的时候,需要做刷新缓存区或者在文件操作结束的时候关闭文件,如果不做,可能导致读写文件的问题。