1、为什么使用文件?
如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失 了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化的保存,我们可以使⽤ ⽂件。
2、文件分类
一般谈的文件有两种,一类是程序文件,一类是数据文件
程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows 环境后缀为.exe)
数据文件又被称为文本文件或二进制文件,此节主要讨论的数据文件
1、文本文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式存储到⽂件中
2、二进制文件
数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中
⼀个数据在⽂件中是怎么存储的呢? 字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽ ⼆进制形式输出,则在磁盘上只占4个字节
3、文件的打开和关闭
1、流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出 操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作。 C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。
2、标准流
stdin-标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
stdout-标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。
stderr-标准错误流,⼤多数环境中输出到显⽰器界⾯
c语言中默认打开了三个流,并且通过FILE* 的⽂件指针来维护流的各种操作的
3、文件指针
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名 字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系 统声明的,取名FILE
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信息
⼀个FILE的指针来维护这个FILE结构的变量,即FILE*pf
4、文件的打开与关闭
FILE*fopen(const char* FILeName, const char* Mode);//打开文件
int fclose(FILE * Stream);//关闭文件
fopen返回值是指针,如果返回失败就返回空指针NULL;
而fcolse返回值是int类型,返回成功返回0,失败返回EOF;
一般来讲,打开文件需要判断是否打开成功,关闭文件需要将文件指针置为空
//打开
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//关闭
fclose(pf);
pf = NULL;
mode表示文件打开模式,即以下模式:
- “r”(只读) 为了输⼊数据,打开⼀个已经存在的⽂本⽂件,如果指定⽂件不存在。出错
- “w”(只写) 为了输出数据,打开⼀个⽂本⽂件 ,如果指定⽂件不存在,建⽴⼀个新的⽂件
- “a”(追加)向⽂本⽂件尾添加数据 ,如果指定⽂件不存在,建⽴⼀个新的⽂件
- “rb”(只读)为了输⼊数据,打开⼀个⼆进制⽂件,如果指定⽂件不存在,出错
- “wb”(只写)为了输出数据,打开⼀个⼆进制⽂件如果指定⽂件不存在,建⽴⼀个新的⽂件
- “ab”(追加)向⼀个⼆进制⽂件尾添加数据,如果指定⽂件不存在,建⽴⼀个新的⽂件
- “r+”(读写)为了读和写,打开⼀个⽂本⽂件,如果指定⽂件不存在,出错
- “w+”(读写)为了读和写,建议⼀个新的⽂件,如果指定⽂件不存在,建⽴⼀个新的⽂件
- “a+”(读写)打开⼀个⽂件,在⽂件尾进⾏读写,如果指定⽂件不存在,建⽴⼀个新的⽂件
- “rb+”(读写)为了读和写打开⼀个⼆进制⽂件,如果指定⽂件不存在,出错
- “wb+”(读 写) 为了读和写,新建⼀个新的⼆进制⽂件,如果指定⽂件不存在,建⽴⼀个新的⽂件
- “ab+”(读 写) 打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写,如果指定⽂件不存在,建⽴⼀个新的⽂件
4、文件的顺序读写
顺序读写函数介绍:
fputc:
返回类型为int类型,写入成功返回对应字符的ASCLL码值,失败则返回EOF,是将字符一个一个写入文件的函数
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fclose(pf);
pf = NULL;
return 0;
}
fgetc:
返回值和fputc一样,但只有文件指针一个参数
fputs:
与fputc不同的是是一次写入的是字符串
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("hello world\n", pf);
fputs("hello world", pf);
fclose(pf);
pf = NULL;
return 0;
}
fgets:
函数原型: char *fgets(char* Buffer, int count_max, FILE * Stream)
fgets是输出文件中的一行,其中第二个参数是读取文件中对应行的字符个数
读取失败返回空指针
fwrite:
函数原型: size_t fwrite(const char* Buffer, size_t ElementSize, size_t ElementCount, FILE * Stream),函数返回值为成功写入元素的个数
int main()
{
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int arr[] = { 1,2,3,4,5 };
fwrite(arr, arr[0], sizeof(arr) / sizeof(arr[0]), pf);
fclose(pf);
pf = NULL;
return 0;
}
fread:
如果读取失败则返回0,读取成功则返回写入数据的个数
5、文件的随机读写:
SEEK_CUR;
SEEK_END;
SEEK_SET;
fseek:
函数原型:int fseek ( FILE * stream, long int offset, int origin )
用来定位文件指针
第三个参数可以是:SEEK_SET(起始),SEEK_END(末尾),SEEK_SET(当前)
第二个参数是相对于origin的偏移量
ftell:
函数原型:long int ftell ( FILE * stream )
返回文件指针相对于起始位置偏移量
rewind:
函数原型:void rewind ( FILE * stream )
让⽂件指针的位置回到⽂件的起始位置
6、文件读取结束的判定
feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束,读取结束,返回非0值,反之返回0
ferror是判断文件是否异常结束,读取异常结束返回非0值,反之返回0
1. ⽂本⽂件读取是否结束,判断返回值是否为 例如:EOF或NULL
2. ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。