一、文件概述
1、文件:文件是计算机文件属于文件的一种,与普通文件载体不同,计算机文件是以计算机硬盘为载体存储在计算机上的信息集合。 文件可以是文本文档、图片、程序等等。
2、文件可以分为:数据文件和程序文件 。
3、文件名:唯一的文件标识,文件名包含三部分:文件路径、文件名、文件后缀。
注意:
- 文件名可以不包含后缀名。
- 文件中不能包含这些字符:\/:*?"<>|。
- 文件路径指的是从盘符到该文件所经历的路径中各符号名的集合。
- 文件后缀名决定了一个文件的默认打开方式。
二、文件的打开与关闭
1、文件指针
文件指针:文件类型指针,简称文件指针。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
我们可以这样定义一个文件指针:
FILE* pf;
定义 pf 是一个指向 FILE 类型数据的指针变量。可以使 pf 指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联 的文件。
2、打开文件
fopen(const char* filename,const char* mode):文件打开函数。
- filename:要打开的文件的文件名称,以字符串的形式。
- mode:文件访问模式,以字符串的形式。
文件访问模式:
演示:
#include<stdio.h> int main() { FILE* pf; pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); } }
我们刚开始是没有这个文件的,但是我们以只写的方式打开文件 ,但是若filename的文件不存在,那么fopen函数会自动创建文件。
#include<stdio.h> int main() { FILE* pf; pf = fopen("test1.txt", "r"); if (pf == NULL) { perror("fopen"); } }
同样是打开的文件不存在,但是如果以只读的方式打开的话,就会打开失败。
所以,当我们打开文件时,若以“读”的模式打开,文件必须存在;若以“写”的模式打开时,文件可以不存在,会自动创建。
3、关闭文件
fclose(const char* stream):文件关闭函数。
- stream:文件指针
演示:
#include<stdio.h> int main() { FILE* pf; pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); } fclose(pf); pf = NULL; return 0; }
注意:
- 在对文件操作完成之后,必须对文件进行关闭。
- 必须对文件指针置为NULL,防止成为野指针。
三、文件顺序读写函数
text.txt的文件内容:
1、fgetc
int fgetc(FILE* stream):字符输入函数,适合所有输入流。
int main() { FILE* pf; pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); } char c = fgetc(pf); printf("%c", c); fclose(pf); pf = NULL; return 0; }
运行结果:
注意:fgetc函数是获取单个字符的,而且每调用一次指针就会自动向后移一位。
2、fputc
int fputc(int character,FILE* stream):字符输出函数,适合所有输出流。
int main() { FILE* pf; pf = fopen("test.txt", "a"); if (pf == NULL) { perror("fopen"); } char c = 'h'; fputc(c,pf); fclose(pf); pf = NULL; return 0; }
运行结果:
3、fgets
int fgets(char* str,int num,FILE* stream):文本行输入函数,适合所有输入流。
- str:读取的字符数组的指针
- num:复制到str的最大字符数
- stream:输入流FILE对象的指针
int main() { FILE* pf; pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); } char str[100] = { 0 }; fgets(str,3,pf); printf("%s", str); fgets(str, 4, pf); printf("%s", str); fclose(pf); pf = NULL; return 0; }
运行结果:
注意:fets函数不会自动换行,只有在读取到'\n' 才会换行。
4、fputs
int fputs(const char* str,FILE* stream):文本行输出函数,适合所有的输出流。
- str:要写入的字符串指针。
- stream: 输出流FILE对象的指针。
int main() { FILE* pf; pf = fopen("test.txt", "a"); if (pf == NULL) { perror("fopen"); } char str1[20] = "igklmn"; fputs(str1,pf); char str2[20] = "12345"; fputs(str2, pf); fclose(pf); pf = NULL; return 0; }
运行结果:
fputs函数也不会自动换行,需要自行在字符串中加上'\n'。
int main() { FILE* pf; pf = fopen("test.txt", "a"); if (pf == NULL) { perror("fopen"); } char str1[20] = "igklmn\n"; fputs(str1,pf); char str2[20] = "12345\n"; fputs(str2, pf); fclose(pf); pf = NULL; return 0; }
运行结果:
5、fscanf
int fcanf(FILE* stream,cost char* format,……):格式化输入函数,适合于所有流。
注意:该函数是从stream所指的文件中以指定形式读取信息到format参数所指的位置。
test2.txt的文件内容:
int main() { struct S s = {0}; FILE* pf = fopen("test2.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score)); printf("%s %d %f\n", s.name, s.age, s.score); fclose(pf); pf = NULL; return 0; }
运行结果:
6、fprintf
int fprintf(FILE* stream,cost char* format,……):格式化输出函数,适合于所有流。
注意:该函数是从stream所指的文件中以指定形式输出信息到format参数所指的位置。
int main() { struct S s = { "强明",15,90}; FILE* pf = fopen("test2.txt", "a"); if (NULL == pf) { perror("fopen"); return 1; } fprintf(pf, "\n%s %d %f", s.name, s.age, s.score); fclose(pf); pf = NULL; return 0; }
运行结果:
7、fwrite
size_t fwrite(void* ptr,size_t size,size_t count,FILE* stream):二进制输出函数,适合于文件类型。
- ptr:指向内存块的指针
- size:每个元素的大小
- count:读取元素的数目
- sream:输入流FILE对象的指针
int main() { struct S s = { "张康",23,77}; FILE* pf = fopen("test3.txt", "wb"); if (NULL == pf) { perror("fopen"); return 1; } fwrite(&s, sizeof(struct S), 1, pf); fclose(pf); pf = NULL; return 0; }
运行结果:
输出文件到文件上显示乱码,原因是我们在写入的时候是以二进制方式,但是打开显示时却不是。
8、fread
size_t fread(void* ptr,size_t size,size_t count,FILE* stream):二进制输入函数,适合于文件类型。
int main() { struct S s = { 0}; FILE* pf = fopen("test3.txt", "rb"); if (NULL == pf) { perror("fopen"); return 1; } fread(&s, sizeof(struct S), 1, pf); printf("%s %d %f\n", s.name, s.age, s.score); fclose(pf); pf = NULL; return 0; }
运行结果:
四、辨析scanf、fscanf、sscanf
scanf:标准的输入流,从标准输入(键盘)读取格式化数据。
fscanf:从所有的输入流读取格式化数据。
sscanf:从字符串中读取格式化数据。
五、文件的随机读写
1、fseek
int feek(FILE* strem,long offset,int origin):文件指针偏移函数。
int main() { FILE* pf = fopen("test3.txt", "w+"); if (NULL == pf) { perror("fopen"); return 1; } char str[20] = "jintianyey"; fputs(str, pf); fseek(pf, 3, SEEK_SET); char ch = fgetc(pf); printf("%c", ch); fclose(pf); pf = NULL; return 0; }
运行结果:
2、ftell
long int ftell ( FILE * stream ):返回指针相对于起始位置的偏移量。
int main() { FILE* pf = fopen("test3.txt", "w+"); if (NULL == pf) { perror("fopen"); return 1; } char str[20] = "jintianyey"; fputs(str, pf); fseek(pf, 3, SEEK_SET); char ch = fgetc(pf); printf("%c\n", ch); int d = ftell(pf); printf("%d", d); fclose(pf); pf = NULL; return 0; }
运行结果:
我们利用了fseek函数设置从开始位置向后偏移三个位置,那么为甚么指针此时的偏移量会是4呢?
我们在fseek函数之后又使用了fputs函数,这个函数获取完单个字符之后,指针会自动向后偏移一个位置。
3、rewind
void rewind ( FILE * stream ):让文件指针的位置回到起始位置。
int main() { FILE* pf = fopen("test3.txt", "w+"); if (NULL == pf) { perror("fopen"); return 1; } char str[20] = "jintianyey"; fputs(str, pf); fseek(pf, 3, SEEK_SET); int d = ftell(pf); printf("%d\n", d); rewind(pf); d = ftell(pf); printf("%d", d); fclose(pf); pf = NULL; return 0; }
运行结果:
六、文本文件和二进制文件
在文件操作中,有的是文本文件,有的是二进制文件,那么两者到底有什么区别呢?
文本文件:数据在内存时以二进制的形式,输出时不加转换就是二进制文件。
文本文件:数据在外存时以ASCII码输入,在存储前需要转换,以ASCII码进行存储的就是文本文件。
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
七、文件读取结束的判定
1、feof
int feof( FILE * stream ):判断文件是否正常结束
当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
注意:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
2、判断文本文件是否正常结束
文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)
int main() { FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } char ch = 0; while ((ch = fgetc(pf)) != EOF) { printf("%c", ch); } if (ferror(pf)) { printf("I/O errors"); } else if (feof(pf)) { printf("Ended successfully"); } fclose(pf); pf = NULL; return 0; }
运行结果:
3、判断二进制文件是否正常结束
二进制文件的读取结束判断,可以利用fread返回值是否小于实际要读的个数来进行判断。
int main() { FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } char ch[10] = { 0 }; while ((fread(ch,1,1,pf))==1) { printf("%s", ch); } if (ferror(pf)) { printf("I/O errors"); } else if (feof(pf)) { printf("Ended successfully"); } fclose(pf); pf = NULL; return 0; }
运行结果:
八、文件缓冲区
ANSIC 标准采用 “ 缓冲文件系统 ” 处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“ 文件缓冲区 ” 。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C 编译系统决定的。