知识点一:文件的存取过程
缓冲区的目的:提高存储效率磁盘使用寿命
知识点二:磁盘文件分类
物理上所有的磁盘文件都是二进制存储,以字节为单位顺序存储
逻辑上的文件分类:
文本文件:基于字符编码的文件,如ASCII、UNICODE等,可以文件编辑器直接打开
二进制文件:基于值编码的文件,数据在内存原样输出到磁盘,需要自己判断或使用特定软件分析数据格式
总结:
(1)译码:文件文件编码基于字符定长,译码容易些。二进制文件编码变长,译码难一些(不同二进制文件格式,有不同的译码方式)
(2)空间利用率:二进制文件用一个比特来代表一个意思(位操作)。文本文件任何一个符号需要一个字节
(3)可读性:文本文件用通用记事本根据几乎可以浏览所有文本文件。二进制文件需要一个具体的文件解码器
知识点三:文件指针
typedef struct
{
short level; //缓冲区“满”或“空”的程度
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如无缓冲区不读取字符
short bsize; //缓冲区大小
unsigned char *buffer; //数据缓冲区的位置
unsigned char *curp; //指针,当前的指向
unsigned istemp; //临时文件,指示器
short token; //用于有效性检查
}FILE;
注意:不要关心FILE的细节只需要会用FILE定义指针变量就行:FILE *fp=NULL;
C语言有三个特殊的文件指针无需定义,打开直接使用:
(1)stdin:标准输入 默认为当前终端(键盘)
使用的scanf、getchar函数默认从此终端获得数据
(2)stdout:标准输出 默认为当前终端(屏幕)
使用的printf、puts函数默认输出信息到此终端
(3)stderr:标准出错 默认当前终端(屏幕)
当我们程序出错或使用:perror函数时信息打印在此终端
知识点四:打开一个文件
形式:
file* fp = null; //定义一个FILE类型的指针接收返回值
fp = fopen(文件名,文件使用方式);
参数:
文件名:要打开的文件名字,可以包含路径信息
文件使用方式:“读”、“写”、“文本”或“二进制”等
返回值:成功返回相应指针,失败返回NULL
知识点五:文件使用方式
1、r:以只读方式打开文件
文件不存在:返回NULL
文件存在:返回文件指针,进行后续读操作
2、w:以只写方式打开文件
文件不存在:以指定文件名创建此文件
文件存在:清空文件内容,进行写操作
若文件打不开(如文件只读),返回NULL
3、a:以追加的方式打开文件(往文件的末尾写入数据)
文件不存在:以指定文件名创建此文件(同w)
文件存在,从文件的结尾处进行写操作
4、+:以可读可写的方式打开
5、b:以二进制的方式打开文件
6、t:以文本的方式打开文件(省略)
知识点六:打开方式的组合形式
模式 | 功能 |
---|---|
r或rb | 以只读方式打开一个文本文件(不创建文件) |
w或wb | 以写方式打开文件(使文件长度截断为0字节,创建一个文件) |
a或ab | 以添加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于写 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ | 以可读、可写的方式打开文件(使文件长度为0字节,创建一个文件) |
a+或ab+ | 以添加方式打开文件,打开文件并在未尾更改文件(如果文件不存在,则创建文件) |
知识点七:关闭文件
形式:fclose(文件指针); //文件指针:指向要关闭的文件
返回值:
关闭文件成功,返回值为0
关闭文件失败,返回值非零
知识点八:文件的读写
1、字节读操作
ch = fgetc(fp); //读一个字节
说明:从指定文件读一个字节赋给ch(以“读”或“读写”方式打开)
文件文件:读到结尾返回EOF
二进制文件:读到文件结尾,使用feof判断结尾
事先本地创建a.text
void test01()
{
char buf[127]="";
int i = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen("a.txt", "r") ;
if(fp == NULL)
{
perror("fopen") ;
return ;
}
//2、对文件的操作
while(1)
{
//fgetc调用一次 读取到一个字节
buf[i] = fgetc(fp);
if(buf[i] == EOF)//EOF表已经对到文件末尾
{
break ;
}
i++;
}
printf("buf = %s\n",buf);
//3、 关闭文件
fclose(fp);
}
2、字节写操作
形式:fputc(ch,fp); //写一个字符
说明:把一个ch变量中的值(1个字节)写到指定文件
如果输出成功,则返回输出的字节
如果输出失败,则返回一个EOF
注意:EOF是在stdio.h文件中定义的符号常量,值为-1
void test01()
{
char buf[127] = "";
int i = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen("a.txt", "w") ;
if(fp == NULL)
{
perror("fopen") ;
return ;
}
//2、使用fputc进行文件的数据写入
printf("请输入要写入文件的字符串:") ;
fgets(buf,sizeof(buf),stdin) ;//会获取换行符
buf[strlen(buf) - 1] = 0;//去掉键盘输入的换行符
//将字符串buf中的元素逐个写入文件中
while(buf[i] != '\0')
{
fputc(buf[i],fp);
i++;
}
//3、 关闭文件
fclose(fp);
}
3、字符串写操作
fputs("china",fp); //写一个字符串
说明:
向指定的文件写一个字符串
第一个参数可以是字符串常量,字符串数组名或字符指针
字符串末尾的'\0'不会写到文件中
void test01()
{
char *buf[] = {"床前明月光\n","疑是地上霜\n"};
int n = sizeof(buf)/sizeof(buf[0]);
int i = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen("a.txt", "w");
if(fp == NULL)
{
perror("fopen");
return ;
}
for(i = 0;i<n;i++)
{
fputs(buf[i],fp);
}
//3、 关闭文件
fclose(fp);
}
4、字符串读操作
获取文件一行的数据
形式:fgets(str,n,fp); //读取一个字符串
说明:从fp指向的文件读入n-1个字符,在读入n-1个字符之前遇到换行符或EOF,读入提前结束,并读取换行符,在最后加一个'\0',str为存放数据的首地址
返回值:
成功:返回读到字符串的首元素地址
失败:返回NULL
void test01()
{
char buf[128] = "";
char *path = "a.txt";
//int i = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"r"); //fp = fopen("a.txt","r");
//fp = fopen("path","r"); //err打开一个文件名叫"path"而不是path指向的文件名"c. txt"
if(fp == NULL)
{
perror("fopen");
return ;
}
while(1)
{
char *ret = NULL;
//从文件中读取一行数据
ret = fgets(buf,sizeof(buf),fp);
if(ret == NULL)
break;
printf("%s\n",buf);
}
//3、 关闭文件
fclose(fp);
}
5、块读写
(1)fwrite数据块写操作
使用 fwrite 将数据块写入到文件中
形式:fwrite(buffer,size,count,fp);
参数说明:
buffer:指向存储数据空间的首地址的指针
size:一次写的数据块大小
count:要写的数据块个数
fp:指向要进行写操作的文件指针
返回值:实际写的数据块数(不是总数据大小)
typedef struct
{
char name[16]; //姓名
int deff; //防御
int atk; //攻击
}HERO;
void test01()
{
HERO hero[] = {{"德玛西亚",80,60},{"盲僧",90,80},{"小法", 40,85},{"小炮",50,90}};
char *path = "a.txt";
//int i = 0;
int n = sizeof(hero)/sizeof(hero[0]);
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"w");
if(fp == NULL)
{
perror("fopen");
return ;
}
//fwrite将内存的数据原样的输出到文件中
//写入文件的数据不便于用户查看但是不会影响程序的读
fwrite(hero,sizeof(HERO),n,fp);
//3、 关闭文件
fclose(fp);
}
(2)fread数据块读操作
使用 fread 从文件中读取数据块
形式:fread(buffer,size,count,fp);
参数说明:
buffer:指向存储数据空间的首地址的指针
size:一次读的数据块大小
count:要读的数据块个数
fp:指向要进行读操作的文件指针
返回值:实际读的数据块数(不是总数据大小)
typedef struct
{
char name[16]; //姓名
int deff; //防御
int atk; //攻击
}HERO;
void test01()
{
HERO hero[4]= {""};
char *path = "a.txt";
int i = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"r");
if(fp == NULL)
{
perror("fopen");
return ;
}
fread(hero,sizeof(HERO),4,fp);
for(i=0;i<4; i++)
{
//printf(" 英雄姓名:《%s》,防御:%d》, 伤害:《%d》\n",hero[i].name,hero[i].deff,hero[i].atk);
printf("英雄姓名:《%s》, 防御:《%d》,伤害:《%d》\n",(hero+i)->name,(hero+i)->deff,(hero+i)->atk);
}
//3、 关闭文件
fclose(fp);
}
6、格式化读操作
读:fprintf(文件指针,格式字符串,输出列表);
typedef struct
{
char name[16];//姓名
int deff;//防 御
int atk;//攻击
}HERO;
void test01()
{
HERO hero[]= {{"德玛西亚",80,60},{"盲僧",90,80},{"小法", 40,85},{"小炮",50,90}};
char *path = "a.txt";
int i = 0;
int n = sizeof(hero)/sizeof(hero[0]);
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"w");
if(fp == NULL)
{
perror("fopen");
return ;
}
for(i= 0;i<n;i++)
{
fprintf(fp,"英雄:%s 防御:%d 攻击:%d\n",hero[i].name,hero[i].deff,hero[i].atk);
}
//3、 关闭文件
fclose(fp);
}
7、格式化写操作
写:fscanf(文件指针,格式字符串,输入列表);
typedef struct
{
char name[16]; //姓名
int deff; //防御
int atk; //攻击
}HERO;
void test01()
{
HERO hero[4]= {""};
char *path = "a.txt";
int i = 0;
//int n = sizeof(hero)/sizeof(hero[0]);
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"r");
if(fp == NULL)
{
perror("fopen");
return ;
}
for(i= 0;i<4;i++)
{
fscanf(fp,"英雄:%s 防御:%d 攻击:%d\n", hero[i].name,&hero[i].deff,&hero[i].atk);
}
for(i=0; i<4; i ++)
{
printf("%s %d %d\n",hero[i].name,hero[i].deff,hero[i].atk);
}
//3、 关闭文件
fclose(fp);
}
注意:
(1)用fprintf和fscanf函数对磁盘文件读写使用方便,但在输入时要将ASCII码转换成二进制形式,在输出时将二进制形式转换成字符,花费时间较多
(2)在内存与磁盘频繁交换数据的情况下,最好不用fprintf和fscanf函数,而用fread和fwrite函数
8、文件随机读写
void test01()
{
char buf[128] ="";
char *path = "a.txt";
int i = 0;
//int n = sizeof(hero)/sizeof(hero[0]);
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"w+");
if(fp == NULL)
{
perror("fopen");
return ;
}
//先往文件中写入一个字符串"hello file"
fputs("hello file",fp);
fclose(fp);
//重新打开文件让文件的流指针 回到文件首部
fp = fopen(path,"r");
//在从文件找那个读取该字符串
fgets(buf,sizeof(buf),fp);
printf("buf = %s\n",buf);
//3、关闭文件
fclose(fp);
}
如果在写的步骤中没有关闭后再打开文件则会读取到右边等的情况
所以需要文件写完后需要关闭文件然后重新打开文件,让文件流指针指向文件开始位置让变下次的文件读操作
9、复位文件流指针
形式:rewind(文件指针);
功能;把文件内部的位置指针移到文件开始位置
void test01()
{
char buf[128] ="";
char *path = "a.txt";
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"w+");
if(fp == NULL)
{
perror("fopen");
return;
}
//先往文件中写入一个字符串"hello file"
fputs("hello file",fp);
//复位文件流指针
rewind(fp);
//在从文件找那个读取该字符串
fgets(buf,sizeof(buf),fp);
printf("buf = %s\n",buf);
//3、 关闭文件
fclose(fp);
}
10、获得文件流指针距离文件首部的字节数
形式:ftell(文件指针);
返回值:返回当前位置(距离文件起始位置的字节数),出错返回-1
void test01()
{
char buf[128] = "";
char *path = "a.txt";
long file_len = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"w+");
if(fp == NULL)
{
perror("fopen");
return;
}
//先往文件中写入一个字符串"hello file"
fputs("hello file",fp);
//获取文件流指针距离文件首部的字节数
file_len = ftell(fp);
printf("file_len = %ld\n",file_len);
//3、关闭文件
fclose(fp);
}
11、定位文件的流指针 fseek函数
形式:fseek(文件类型指针,位移量,起始点); //一般用于二进制文件
功能:移动文件中位置指针的位置
参数说明:
位移量:相对 起始点 的偏移量,向前、向后移动的字节数(-10往左边移动10字节,+10往右边移动10字节)
起始点:开始添加偏移 位移量 的位置
位置指针的位置说明:
在文件开头:0 (SEEK_SET)
在文件当前位置:1 (SEEK_CUR)
在文件末尾:2 (SEEK_END)
一次性读取文件总数
void test01()
{
char *file_data = NULL;
long file_len = 0;
char *path = "a.txt";
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"r");
if(fp == NULL)
{
perror("fopen");
return ;
}
//需求:一次性的将文件数据读取到内存中
//1、得到文件的总大小
//a、 使用 fseek 将文件指针定位到文件尾部
fseek(fp,0,2);
//b、使用 ftell 计算文件的偏移量==文件的总大小
file_len = ftell(fp);
//c、使用rewind复位文件流指针
rewind(fp);
//2、根据文件的总大小合理申请内存空间
file_data = (char *)calloc(1,file_len+1); //+1的目的 内存末尾存放'\0'
if(file_data == NULL)
{
fclose(fp);
return;
}
//3、一次性将文件数据读入到内存空间
fread(file_data,file_len,1,fp);
//4、遍历读取到的文件内容
printf("file_len = %ld\n",file_len);
printf("%s\n",file_data);
//5、释放堆区空间
if(file_len != NULL)
{
free(file_len);
file_len = NULL;
}
// 关闭文件
fclose(fp);
}
知识点九:feof()函数判断文件是否到达文件末尾
EOF宏:只能用于文本文文件
feof函数:可以用于文本文文件 二进制文件
判断文件是否结束:0:未结束,非0:表示结束
void test01()
{
char *path = "a.txt";
int i = 0;
FILE *fp = NULL;
//1、 使用fopen打开一个文件获得文件指针
fp = fopen(path,"r");
if(fp == NULL)
{
perror("fopen");
return ;
}
//feof(fp)判断文件是否结束 0:未结束,非 0:表示结束
//while(feof(fp) == 0)//文件未结束才循环
while( !feof(fp))
{
char ch = fgetc(fp);
printf("%c",ch);
}
// 关闭文件
fclose(fp);
}
知识点十:文件加密器
加密过程:
解密过程: