什么是文件
将数据放入文件中,相比代码程序中堆栈上的数据,其优点在于可以随时做到需要时添加、舍弃时删除,数据可以持久化。
文件类型:
一般分为:程序文件与数据文件。
程序文件:
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
数据文件:
文件的内容不一定是程序,而是程序运行中的读取的数据。本章讨论的是数据文件。
文件的使用
在了解文件怎样使用前,我们先要了解流与标准流的概念
流
程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
这样程序员在输入输出数据时不需要关心外部设备了,只需要将数据与流相连,C语言底层已经将流向不同外部设备的输入和输出实现好了,不需要我们关心。
我们只需要从流里读取数据,向流里写数据。
标准流
那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动时已经将标准流(standard stream)准备好了。
默认打开了3个流:
stdin-标准输入流,在大多数的环境中从键盘输入,scanf函数就是从stdin-标准输入流(键盘)中读取数据。
stdout-标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到stdout-标准输出流(屏幕)中。
stderr-标准错误流,大多数环境中输出到显示器界面。
stdin、 stdout、 stderr 三个流的类型是: FILE*,通常称为文件指针。
C语言中,就是通过 FILE*的文件指针来维护流的各种操作的。
文件的操作
文件操作分为三步:打开文件(fopen)、读取或写入文件、关闭文件(fclose)。
文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名 字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系 统声明的,取名FILE.
FILE*pf;//文件指针变量pf
定义一个指针变量指向FILE类型数据,可以使pf指向某个文件的文件信息区(是一个结构体变 量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件
文件的打开与关闭
文件读写之前应该先打开文件,使用结束后应该关闭文件。
ANSIC 规定使用 fopen 函数来打开文件, fclose 函数来关闭文件。
在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指
针和文件的关系。
fopen() —— 打开文件;
FILE * fopen ( const char * filename, const char * mode );
fclose() —— 关闭文件;
int fclose ( FILE * stream );
例:
#include<stdio.h> #include<assert.h> int main() { FILE* pr = fopen("test.txt","w"); //当没有文件时读取,fopen输出NULL //当没有文件写入时,会自动创建文件 assert(pr); //这里使用assert断言,以防pr为空,也可以使用perror函数 for (char ch = 'a'; ch <= 'z'; ch++) { fputc(ch, pr);//fputc函数将字符ch存入pr文件中 } fclose(pr); return 0; }
文件的使用方式:
按常用序:
文件的顺序读写:
1.写入一个字符
fgetc(字符c,文件指针);
2.读取一个字符
fputc(文件指针); //每一次读取光标回向后移动一位 例:test.txt文件里面:abcdefg char ch=fputc(pr); printf(%c,ch);//输出为a; char ch=fputc(pr); printf(%c,ch);//输出为b;
3.写入一个字符串
fgets("hello,world",文件指针); //每次的写入会导致数据的覆盖,在原先的数据后继续添加,需要使用a(追加);
4.读取指定长度的数据(读取数组)
char arr[10]={0}; fgets(arr,5,文件指针);//将所读取的数据存入arr数组中
5.读取结构体指定数据(fprintf);
#include<stdio.h> typedef struct S//typdef,以便更好的创建结构体变量 { char name[10]; int age; }S1; int main() { FILE* pf = fopen("test.txt", "w"); if (pf != NULL)//也可用assert函数判断是否为空 { S1 s1 = { "zhangsan", 18 };//创建变量s1 fprintf(pf, "%s %d\n", s1.name, s1.age); fclose(pf); pf = NULL; } return 0; }
文件的随机读写
fseek函数
fseek - C++ 参考 (cplusplus.com)
int fseek ( FILE * stream, long int offset, int origin );
流 偏移量 起始位置
流:指向标识流的 FILE 对象的指针。
偏移量:二进制文件:要从源偏移的字节数。文本文件:零或 ftell 返回的值。
如果成功,该函数将返回零。
否则,它将返回非零值。
如果发生读取或写入错误,则设置错误指示符 (ferror)。
三种定位指针
#include<stdio.h> int main() { FILE* pFile; pFile = fopen("test.txt", "r"); //fputs("This is an apple.", pFile); fseek(pFile,2, SEEK_SET);//从起始位置的第二个偏移量 int ch = fgetc(pFile); printf("%c", ch); fclose(pFile); return 0; }
注意:
ftell函数
返回文件指针相对起始位置的偏移量
long int ftell ( FILE * stream );//注:返回为long int类型
#include <stdio.h> int main() { int n; FILE* pFile; char buffer[27]; pFile = fopen("test.txt", "w+"); for (n = 'A'; n <= 'Z'; n++) fputc(n, pFile);//向文本中输入字符 rewind(pFile);//让文件指针回到起始位置 fread(buffer, 1, 26, pFile);//读取1到26个数据 fclose(pFile); buffer[26] = '\0'; printf(buffer);//验证 return 0; }
feof函数(文件读取结束的判定)
feof - C++ 参考 (cplusplus.com)
文本文件:
- fgetc 判断是否为 EOF .
- fgets 判断返回值是否为 NULL
二进制文件:
都是使用 fread() 读取,要判断其返回值与指定读取个数的大小,如果小于实际要读的个数,就说明发生读取异常,如果等于实际要读的个数,就说明是因读取成功而结束;
对于读取异常的判断,我们考虑判断 ferror() 函数的返回值:
若ferrror()为真——异常读取而结束;
若feof()为真——正常读取到尾而结束;
二进制文件和文本文件
我们知道数据在内存中是以二进制形式存储的,对于文件而言:如果不加转换直接输出到外存就是二进制文件;如果要在外存上以ASCII码形式存储,就需要提前转换最后以ASCII码值形式存储的文件就是文本文件。
对于字符,一律使用ASCII码形式存储,但对于数值型数据,即可以使用ASCII码存储也可以使用二进制形式存储。
oi!点个赞走吧!!!