目录
1.文件
文件时存放在磁盘上的。一般是分为程序文件、数据文件。
1.1程序文件
包括源程序文件(.c),目标文件(windows环境后缀为.obj),可执行程序(win环境后缀是.exe)
1.2数据文件
文件内容不一定是程序,也可能是程序运行时读写的数据。有时数据处理完后,不一定要打印到屏幕上,而是存到磁盘上,用时再读取到内存中,这里处理的就是磁盘上文件
1.3文件名
文件名包括3部分,路径+名字+文件后缀
2.二进制文件和文本文件
根据组织形式,数据文件称为文本文件或二进制文件
数据在内存中以二进制存储,不加转换,直接输出到外存就是二进制文件
如果在存储前进行了转换,那么就会以ASCLL字符的形式存储在外存,这个文件就是文本文件。
字符用ASCLL存储,数值可ASCLL或直接二进制存储
同样一个数,用ASCLL和二进制存储,占用空间不一定一样,因为ascll字符一个字符一个字节,而二进制要看数据大小
3.文件打开和关闭
3.1流和标准流
3.1.1流
流可看作是流着字符的河,因为不同外部设备的输入输出操作方式都不同。
c对文件、画面、键盘等的数据输入输出都是同流操作的
一般情况下,我们向流写数据或读数据,都是打开流,再操作
3.1.2标准流
平时我们启动代码,从键盘输入内容,再输出内容。c语言程序看起来没有打开流,但默认是已经打开了3个流。
stdin-标准输入流,大多数环境中支持从键盘输入数据;
stdout-标准输出流,大多数环境中支持输出至显示器;
stderr-标准错误流,大多数环境中输出到显示器界面
因为这3个流默认打开,所以我们才能使用scanf和printf等函数直接输入输出,3个流的类型是FILE*,称为文件指针
3.2文件指针
缓冲文件系统中,关键的概念是文件类型指针,简称文件指针
每个被使用的文件在内存中都开辟了一个相应的文件信息区,用来存放文件的相关信息(文件的名字,文件状态、文件当前位置等)。保存在一个结构体变量,该结构体类型是系统声明的,即FILE。
当打开一个文件时,系统会根据文件的情况自动创建FILE结构的变量,并填充其中的信息。
一般用FILE的指针来维护FILE结构的变量。
FILE*的指针变量 FILE*pf; 这里是将FILE变量的地址存入pf,形成指针 解引用pf可以指向某文件的文件信息区(这个文件信息区是个结构体变量) 通过该文件信息区的信息可以访问该文件,通过文件指针变量能间接找到相关联的文件
3.3文件打开和关闭
文件读写前应该先打开文件,使用结束后应该关闭文件
编写程序时,打开文件的同时会返回一个FILE*的指针变量指向该文件。
ANSIC规定使用FOPEN函数打开文件,fclose关闭文件
FILE* fopen(const char filename,const char*mode); 打开文件,mode参数是文件的打开方式,打开失败会返回空指针 r是只读,为了输入数据,打开提前存好的文本文件 w是只写,为了输出数据,打开一个文本文件并且会清空这个文件文件的原内容, 如果文件不存在则新建一个文本文件 文件名可以是绝对路径,也可以是相对路径 int fclose(FILE*stream); 关闭文件,但指针变量本身还存着地址,虽然那个地址已经不代表文件信息了 但还是要让指针变量变成空指针,从而避免成为野指针
4.文件的顺序读写
顺序读写有一系列函数
fgetc函数,字符输入,所有输入流,一次读取一个字符 返回类型是int,也就是输入的字符的ascll码值,如果输入错误,则返回EOF 每次读取一个字符,都会跳到之后一个字符 fgetc适合所有,而getchar只适合标准输入 fputc,字符输出,所有输出,一次写一个字符 fgets文本行输入,所有输入,一次读一行字符 一次只读取一行字符,就算num参数大于可读取的字符数,也只是读取有的字符数 fputs文本行输出,所有输出,一次写一行字符 所有包裹了文件流和屏幕,只要将对应文件写成标准输出流就行 fprintf,只要按照printf的格式写,最后前面加个要输出的文件或者输出流即可。 结构体也可以借此输入输出 fscanf用法和fprintf类似 以上都是所有输入或输出流 fwrite的参数分别是,(写入的数据来源,数据单个的大小,数据的数量,指定要写入的文件) fread的参数(读出的数据存放位置,数据单个大小,数据数量,指定读出数据的文件) 返回的是实际读取的个数
4.1函数对比
对于scanf和fscanf和sscanf函数还有printf和fprintf和sprintf。
scanf-针对标准输入流,printf针对标准输出流
fcanf-针对所有输入流,fprintf针对所有输出流
sprintf是将要打印的内容,按照对应格式,以字符的形式打印到指定的流中
sscanf用法类似sprintf,是从指定的流中,将字符串中提取出对应格式的数组
5.文件的随机读写
5.1fseek
int fseek(FILE*stream,long int offset,int origin); 根据文件指针的位置,还有偏移量,来定位文件指针 因为我们一般读取写入都是从文件的一个字符开始,这个函数可以改变 指定的位置 offset是偏移量 origin参数,有3个,一个是从SEEK_SET,也就是从文件第一个字符开始偏移 SEEK_END是从文件最后一个字符开始偏移 SEEK_CUR是指从文件指针当前的位置开始偏移(因为如果你读取过或写入过,那么 文件指针的位置就会改变) #include <stdio.h> int main () { FILE * pFile; pFile = fopen ( "example.txt" , "wb" ); fputs ( "This is an apple." , pFile ); fseek ( pFile , 9 , SEEK_SET ); fputs ( " sam" , pFile ); fclose ( pFile ); return 0; }
5.2ftell
long int ftell(FILE *stream); 可以返回文件指针当前位置对于文件起始位置的偏移量 /* ftell example : getting size of a file */ #include <stdio.h> int main () { FILE * pFile; long size; pFile = fopen ("myfile.txt","rb"); if (pFile==NULL) perror ("Error opening file"); else { fseek (pFile, 0, SEEK_END); // non-portable size=ftell (pFile); fclose (pFile); printf ("Size of myfile.txt: %ld bytes.\n",size); } return 0; }
5.3rewind
void rewind(FILE *stream); 让文件指针回到文件的起始位置 /* rewind example */ #include <stdio.h> int main () { int n; FILE * pFile; char buffer [27]; pFile = fopen ("myfile.txt","w+"); for ( n='A' ; n<='Z' ; n++) fputc ( n, pFile); rewind (pFile); fread (buffer,1,26,pFile); fclose (pFile); buffer[26]='\0'; printf(buffer); return 0; }
7.文件读取结束的判定
7.1feof
在文件读取过程中,不能用feof函数的返回值直接判断文件是否结束
feof:当文件读取结束的时候,判断读取结束的原因是否是:遇到文件尾结束。
文件读取结束可能是遇到文件末尾,也可能是文件读取错误。
正常文件结束判断:
文本文件读取是否结束,判断返回值是否是EOF(fgetc),或者NULL(fgets)
二进制文件的读取结束判断:
判断返回值是否小于实际要读的个数
fread判断返回值是否小于实际要读的个数
ferror是判判是否是文件读取错误而结束读取
#include <stdio.h> #include <stdlib.h> int main(void) { int c; // 注意:int,⾮char,要求处理EOF FILE* fp = fopen("test.txt", "r"); if(!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环 { putchar(c); } //判断是什么原因结束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
#include <stdio.h> enum { SIZE = 5 }; int main(void) { double a[SIZE] = {1.,2.,3.,4.,5.}; FILE *fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式 fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组 fclose(fp); double b[SIZE]; fp = fopen("test.bin","rb"); size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组 if(ret_code == SIZE) { puts("Array read successfully, contents: "); for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]); putchar('\n'); } else { // error handling if (feof(fp)) printf("Error reading test.bin: unexpected end of file\n"); else if (ferror(fp)) { perror("Error reading test.bin"); } } fclose(fp); }
8.文件缓冲区
ANSIC标准采用 缓冲文件系统 处理数据文件,缓冲文件系统指 系统自动地在内存中为程序中每一个正在使用的文件开辟一块文件缓冲区。从内存向磁盘输出数据,会先送到内存中的缓冲区,装满缓冲区,才一起送去磁盘。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据到内存缓冲区,待装满后,从缓冲区逐个将数据送到程序数据区(程序变量等)。缓冲区大小根据c编译系统决定。
#include <stdio.h> #include <windows.h> //VS2019 WIN11环境测试 int main() { FILE*pf = fopen("test.txt", "w"); fputs("abcdef", pf);//先将代码放在输出缓冲区 printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n"); Sleep(10000); printf("刷新缓冲区\n"); fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘) //注:fflush 在⾼版本的VS上不能使⽤了 printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n"); Sleep(10000); fclose(pf); //注:fclose在关闭⽂件的时候,也会刷新缓冲区 pf = NULL; return 0; }
因为缓冲区,所以c语言操作文件时,需要刷新缓冲区或关闭文件,来达成写入或读取的操作