目录
为什么使用文件
文件名
文件指针
流
文件的打开和关闭
前言
文件的打开方式
文件打开关闭函数
fopen函数
fclose函数
文件的顺序读写
fputc函数
fgetc函数
fputs函数
fgets函数
fprintf函数
fscanf函数
fwrite函数
fread函数
文件的随机读写
fseek函数
ftell函数
rewind函数
文本文件和二进制文件
一个数据在内存中怎么储存
文件读取结束的判定
feof函数
文件缓冲区
理解:
fflush函数
使用文件我们可以将数据直接存放在电脑硬盘上,做到了数据的持久化
在程序设计中,我们一涉及的两种文件
程序文件:包括源文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀名为.exe)
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行时需要从中读取数据的文件,或者输出内容的文件
注意:本章讨论的主要是数据文件
定义:一个文件有一个唯一的文件标识,以便用户识别和引用
文件名包含3部分:文件路径+文件名主干+文件后缀
eg:c:\code\test,txt
定义:缓冲文件系统中,关键的概念是文件类型指针,简称文件指针。
关于FILE
每个被使用的文件都在内存中开辟一个文件信息区,用来存放文件的相关信息(如文件名,文件状态以及文件的当前位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名为FILE
关于VS2013编译环境提供的stdio.h头文件中有以下的文件类型申明
typedef struct _iobuf {
char* _ptr; //文件输出的下一个位置
int _cut; //当前缓冲区的相对位置
char* _base; //指基础位置,文件的起始位置
int _flag; //文件标志
int _file; //文件有效性检查
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; //文件的大小
char* _tmpfname; //临时文件名
}FILE;
注意:
创建一个FILE* 的指针变量
FILE* pf;//文件指针变量
定义的pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。
通过文件指针变量可以找到与他相关联的文件
流:程序数据可能要操作各种各样的硬件(如:硬盘、光盘、u盘等)硬件的不同导致了其读写方式的不同,因为要记硬件读写方式的成本过高,所以就在中间层抽象一个流(其是一个标准)的概念,此时程序向流里面输入数据,然后流再把相应的数据写到我们的不同设备中,因而我们程序员就不用关心硬件读写方式了。
C语言程序只要运行起来,就默认打开了3个流
当用fopen打开一个文件的时候,其会主动创建该文件的文件信息区 ,并且把这个文件信息区的起始地址返回过来,如果打开失败则返回null
参数:filename:传入的文件名 mode:文件的打开方式
注意:使用fclose函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区
传入参数为文件类型的指针
返回值:如果成功关闭则返回0,否则返回eof(-1)
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("C:\\All\\test.txt", "w");//(绝对路径)打开文件
if (pf==NULL)
{
perror("fopen");
}
fclose(pf);//关闭文件
pf = NULL;
}
注意:内存中的数据输出到硬盘中的过程叫写入,硬盘中的数据输入到内存中的过程叫读取
写一个字符到一个流里面,具体要写ASCII码值为c的字符,stream是具体写到哪个地址的文件中
当写入成功时返回写入的字符,当写入失败时返回EOF(文件结束标志)
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
char c[] = "hello world";
for (int i = 0; i <= 11; i++)
{
printf("%c",fputc(c[i], pf));
}
fclose(pf);//关闭文件
pf = NULL;
}//hello world
在stream文件地址处的文件里读取一个字符,如果读取正常就返回该字符的ASCII码值,如果读取失败或遇到EOF则返回EOF
注意:当打开文件并使用fgetc函数读取文件时,fgetc函数从文件的开始位置读取一个字符,每读取一个字符后文件的位置指针向后移动一个字符的位置。若当前读取的是文本文件,当遇到文件结束时返回值为EOF
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
//读取文件
printf("%c", fgetc(pf));//h
printf("%c", fgetc(pf));//e
printf("%c", fgetc(pf));//l
printf("%c", fgetc(pf));//l
printf("%c", fgetc(pf));//o
fclose(pf);//关闭文件
pf = NULL;
}
写一个string字符串到stream文件指针所指向的文件中,如果写入成功则返回非0(默认为1),如果写入错误则返回EOF
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
//写入文件
fputs("hi", pf);
fputs(" world", pf);
fclose(pf);//关闭文件
pf = NULL;
}//hi world(写入的是1行如果需要换行则需要在字符串中加'\n')
在stream指针所指向的文件中读取最多n个字符放到string地址所指向的空间中,返回该地址的指针,如果读完或出现错误则返回null
注意:如果n=100,那么读取的实际有99个非'\0'字符,最后一个放'\0'
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
//读取文件
char arr[6] = { 0 };
printf("%s\n", fgets(arr, 6, pf));//hi wo
printf(arr);//hi wo
printf("%s\n", fgets(arr, 6, pf));//rld
printf(arr);//rld
fclose(pf);//关闭文件
pf = NULL;
//由此观之再次读取时用该数组接受时数组数据被覆盖
}
将agars(参数表)内各项的值,按format(格式控制字符串)所表示的格式,将数据格式为字符串的形式写入到文件指针stream所指向的文件中,fprintf函数返回值是输出的字符数,发生错误时返回一个负值
使用:
引入头文件:#include
#include
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = { "hello",10,5.5f };
fprintf(pf, "%s %d %f", s.arr, s.num, s.sc);//hello 10 5.500000
fclose(pf);//关闭文件
pf = NULL;
}
将stream指针所指向的文件的数据以format(格式控制字符串)的形式 读取出来,放到argu各种参数中,对于返回值fscanf函数在没有出错的情况下返回实际读取数据的个数,如果出错或者读到结尾则返回EOF
使用:
引入头文件:#include
#include
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = {0};
fscanf(pf, "%s %d %f", s.arr, &(s.num), &(s.sc));//第一个也可以&(s.arr)没有取址是因为结构体地址与第一个元素地址0偏移
printf("%s %d %f\n", s.arr, s.num, s.sc);//hello 10 5.500000
fclose(pf);//关闭文件
pf = NULL;
}
作用:将一块内存区域中的数据(buffer所指向的内存区域)以二进制的形式写入到本地文本(stream指向的文件)其返回值返回的是实际写入到文件的基本单元个数
-- buffer:指向数据块的指针
-- size:每个数据的大小,单位为Byte(例如:sizeof(int)就是4)
-- count:数据个数(最多)
-- stream:文件指针
使用:
引入头文件:#include
#include
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = {"hi",10,5.5f};
//二进制形式写文件
fwrite(&s, sizeof(struct Stu), 1, pf);
fclose(pf);//关闭文件
pf = NULL;
}
将stream指针所对应的文件数据读取到buffer指针所对应的内存区域中,size为单个数据的大小,count为数据的个数,该函数的返回值为实际读取到元素的个数,如果发现要读取到完整的元素个数小于指定的元素个数,这就是最后一次读取,读取失败返回0
使用:
引入头文件:#include
#include
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = {0};
//二进制形式读文件
fread(&s, sizeof(struct Stu), 1, pf);
printf("%s %d %f\n", s.arr, s.num, s.sc);//hi 10 5.500000
fclose(pf);//关闭文件
pf = NULL;
}
根据文件指针的位置和偏移量来定位文件指针(调整文件指针)
参数:
stream——针对stream文件地址所对应的文件
offset——偏移量【字节为单位】(负数为向左偏移,正数为向右偏移)
origin——起始位置(从哪开始偏移)
关于起始位置有3个参数
返回值:成功则返回0,否则返回其他值
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("test.txt", "r");
if (pf==NULL)
{
perror("fopen");
return;
}
//读取文件
int ch = fgetc(pf);//pf所指向的文件内容为abcdefg
printf("%c\n", ch);
//调整文件指针
fseek(pf, -1, SEEK_CUR);//pf指向的文件,其指针从当前位置向左偏移1字节
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
}//没加fseek函数时为abc,加了之后为aab
//注意如果遇到换行的情况,说明前一行的字符串末尾有'\0'其也会记在偏移量中
返回文件指针相对于起始位置的偏移量
让文件指针的位置回到文件的起始位置
使用:
引入头文件:#include
#include
void main() {
FILE* pf = fopen("test.txt", "r");
if (pf==NULL)
{
perror("fopen");
return;
}
//读取文件
int ch = fgetc(pf);//pf所指向的文件内容为abcdefg
printf("%c\n", ch);
//调整文件指针
fseek(pf, 1, SEEK_CUR);//pf指向的文件,其指针从当前位置向左偏移1字节
int a = ftell(pf);//告知文件指针相对于起始位置的偏移量
printf("%d\n",a);//2
rewind(pf);//让文件指针的位置回到文件的起始位置
printf("%d\n",ftell(pf));//0
//关闭文件
fclose(pf);
pf = NULL;
}
根据数据的组织形式数据文件称为文本文件和二进制文件
二进制文件:数据在内存中以二进制的形式储存,如果不加转换的输出到外存,就是二进制文件
文本文件:如果要求在外存上以ASCII码的形式储存,则需要在储存前转换,以ASCII字符形式储存的文件就是文本文件
字符一律以ASCII码形式储存,数值型数据既可以用ASCII码形式储存,也可以用二进制形式储存。
10000储存方式
二进制:(共占用4字节)
ASCII形式:(共占5字节)
关于ASCII形式:共5字节每个字节都代表一个ASCII码值分别为(1、0、0、0、0)的ASCII码值
应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾部结束
使用时需引入头文件:#include
返回值:若达到文件尾部EOF则返回非0;否则返回0
注意:文件结束标志——end of file(EOF——值为-1)
调用该函数会将缓冲区中的内容写到stream所指向的文件中去(对于内存缓冲区来说是清空,对于硬盘来说是写入),若stream为null,则会将所有打开的文件进行数据更新。
返回值:如果成功刷新则返回0,指定的流没有缓冲区或者只读打开时也返回0,错误返回-1;
使用:
引入头文件:#include
#include
#include
void main() {
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10s-已经写数据了,发现test.txt里没数据\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区,将输出缓冲区的数据写到文件
printf("睡眠10s再次打开test.txt,有内容了。\n");
Sleep(10000);
//关闭文件也会刷新缓冲区,所以才有第二次睡眠,因为要确定刷新缓冲区的作用
fclose(pf);
pf = NULL;
}
注意:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束时关闭文件(文件关闭时也会刷新缓冲区),如果不做可能会导致读写文件的问题。