🍁🍁🍁 猛戳订阅 👉 详解数据结构专栏 👈 纯C解析 🍁🍁🍁
“东篱把酒黄昏后,有暗香盈袖。”
“莫道不销魂。帘卷西风,人比黄花瘦。”
这里写目录标题
文件的顺序读写
字符输入输出fgetc和fputc
文本行输入输出函数fgets和fputs
格式化输入输出函数fscanf和fprintf
二进制输入输出函数fread和fwrite
文件的随机读写
fseek
ftell
fwind
文本文件和二进制文件
文件结束的判定
feof
文件缓冲区
第一篇讲了文件的基本概念,和文件如何打开和关闭。第二篇主要介绍文件的顺序读写和随机读写。外加文件缓冲区的知识点。
文件的顺序读写
字符输入输出fgetc和fputc
fgetc:字符输入函数,也就是读文件时用的函数。
函数功能:Read a character from a stream
从一个文件中读一个字符到内存中。
函数原型:
int fgetc( FILE *stream );
参数为stream,也就是文件指针。
返回值:fgetc return the character read as an int or return EOF to indicate an error or end of file.
该函数调用成功会返回读取到的的字符的ASCIIC码值;若读取文件时发生错误,或是已经读取到文件末尾,则返回EOF。
举例:将data.txt文件中的内容读取,并打印。
#include <stdio.h> #include <string.h> #include <errno.h> int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); return 0;//文件打开失败,失败返回 } //对文件进行输入字符操作 int ch = 0; while ((ch = fgetc(pf))!= EOF) { printf("%c", ch); } //关闭文件 fclose(pf); pf = NULL; return 0; }
fputc:
函数功能:从内存中写一个字符到文件中。也就是输出字符。
函数原型:
int fputc( int c, FILE *stream );
第一个参数为待输出的字符,第二个参数是文件指针。
返回值:Each of these functions returns the character written. For fputc , a return value of EOF indicates an error.
如果正常运行则返回此字符,如果返回EOF则意味着失败。
举例:将字母a~z写入到data.txt文件中
#include <stdio.h> #include <string.h> #include <errno.h> int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); //文件打开失败,失败返回 if (pf == NULL) { printf("%s\n", strerror(errno)); return 0; } //对文件进行输出字符的操作 char i = 0; for (i = 'a'; i <= 'z'; i++) { fputc(i, pf); } //关闭文件 fclose(pf); pf = NULL; return 0; }
文本行输入输出函数fgets和fputs
fgets和fputs是对文本行的操作,相当于对字符串的操作。这是不同于fgetc和fputc的地方。
fgets:函数功能:
从文件中读一行字符到内存中,也就是输入。
函数原型:
char *fgets( char *string, int n, FILE *stream );
第一个参数是指向文件字符串的指针,第二个参数是读几个字符的意思,第三个参数是指向文件的指针。
返回值:Each of these functions returns string. NULL is returned to indicate an error or an end-of-file condition. Use feof or ferror to determine whether an error occurred.
每一个这样的函数结束后正常情况返回一个指向这个字符串的字符指针。如果返回NULL则意味着遇到错误,或者是文件结束。但是重点来了~,想要判断到底是错误导致的返回,还是文件结束导致的返回,还需要使用feof函数和ferror函数来判断。
下面有涉及feof函数的用法
举个例子。
//feof是用来在结束后判断是什么原因结束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
注意:
1.在fgets函数读取到指定字符数之前,若读取到换行符(’\n’),则停止读取,读取带回的字符包含换行符。
2. fgets函数读取到第n-1个字符时都没有遇到换行符(’\n’)时,则返回读到的前n-1个字符,并在末尾加上一个NULL字符返回。这样加起来共n个字符
fputs函数:
函数功能:
写一行字符串到文件中,也就是输出。
函数原型:
int fputs( const char *string, FILE *stream );
第一个参数为指向内存中这个字符串的指针,第二个参数为指向这个文件的文件指针。
返回值:该函数调用成功会返回一个非负值;若输出时发生错误,则返回EOF。
格式化输入输出函数fscanf和fprintf
fscanf和fprintf也叫格式化输入(读)和输出(写)函数。
fscanf:
函数功能:
按照一定的格式如%s,%c,从指定文件的位置输入到内存中。
函数原型:
int fscanf( FILE *stream, const char *format [, argument ]... );
fscanf函数的第一个参数是读取数据的位置也就是文件指针,第二个参数也就是scanf函数的参数,也就是取地址。
除了第一个参数是需要指针位置其余和scanf函数操作一样。
fprintf:
函数功能:
将内存中的数据以一定的格式输出到文件中。也就是打印,也称为写。
函数原型:
int fprintf( FILE *stream, const char *format [, argument ]...);
第一个参数是文件指针,第二个参数和printf函数一样,会用printf函数就会用这个函数。
举例:
include <stdio.h> #include <string.h> #include <errno.h> struct S { char name[20]; char sex[5]; int age; }; int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); //如果文件打开失败,失败返回 if (pf == NULL) { printf("%s\n", strerror(errno)); return 0; } //对文件进行格式化输入输出操作 struct S tmp = { 0 }; fscanf(pf, "%s %s %d", tmp.name, tmp.sex, &(tmp.age)); printf("%s %s %d\n", tmp.name, tmp.sex, tmp.age); //可以打印出来,我这里没打印。 //关闭文件 fclose(pf); pf = NULL; return 0; }
二进制输入输出函数fread和fwrite
函数功能:Reads data from a stream.从一个流中读取数据到内存中。
函数原型:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
第一个参数是buffer是内存的意思,第二个参数是要读的数据的类型,第三个参数是读取的个数,第四个参数是文件指针。总的意思是从文件指针指向的文件读取count个size大小的数据到内存buffer中。
返回值:若在读取过程中发生错误或是在未读取到指定元素个数时读取到文件末尾,则返回一个小于count的数。
fwrite:
函数功能:Writes data to a stream.写入二进制数据到文件中
函数原型:
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
第一个参数是输出数据的位置,第二个参数是要输出数据的元素个数,第三个参数是每个元素的大小,第四个参数是数据输出的目标位置。
返回值:该函数调用完后,会返回实际写入目标位置的元素个数,当输出时发生错误或是待输出数据元素个数小于要求输出的元素个数时,会返回一个小于count的数。
举例:以wb输出到文件
#include <stdio.h> #include <string.h> #include <errno.h> int main() { //打开文件 FILE* pf = fopen("data.txt", "wb"); if (pf == NULL) { printf("%s\n", strerror(errno)); return 0; } //对文件以二进制形式进行输出操作 int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; fwrite(arr, sizeof(int), 10, pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
文件的随机读写
fseek
函数介绍:
定位文件指针,以文件指针当前的位置,偏移到想定位的位置。向前偏移是负的,比如-1,-2,向后偏移是正的。
SEEK_CUR:从当前指针的位置偏移
SEEK_SET:从文件的起始位置开始偏移
SEEK_END:从文件的末尾开始偏移
函数原型:
int fseek( FILE *stream, long offset, int origin );
第一个参数是文件指针,第二个参数是要偏移的偏移量。第三个参数是从什么位置开始偏移。
举例。
/* fseek example */ #include <stdio.h> int main () { FILE * pFile; //打开文件 pFile = fopen ( "example.txt" , "wb" ); //以一行的形式写文件 fputs ( "This is an apple." , pFile ); //让文件指针从文件的起始位置开始偏移9个单位。 fseek ( pFile , 9 , SEEK_SET ); //继续写文件 fputs ( " sam" , pFile ); //关闭文件 fclose ( pFile ); return 0; }
ftell
函数介绍:
可以返回文件指针相对于起始位置的偏移量
函数原型:
long ftell( FILE *stream );
返回值类型为long int,第一个参数是文件指针。
fwind
函数介绍
让文件指针回到文件的起始位置。fseek函数也可以达到同样的效果。
函数原型:
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'; puts (buffer); return 0; }
文本文件和二进制文件
数据文件:可以分为文本文件和二进制文件
二进制文件:文本文件可以肉眼看懂,二进制文件则是乱码看不懂。
数据在内存中是以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件
文本文件:如果要求在外存中以ASCII码的形式存储,则需要在存储前转换。如果以ASCII字符的形式存储文件就叫做文本文件。
具体例子如下。
一个数据在内存中是怎么存储的呢?
如果整数10000以ASCII码的形式输出到磁盘,则占用5个字节。如果以二进制形式输出到磁盘则占用4个字节。
文件结束的判定
feof
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件是否结束。
函数功能:
应用于当文件读取结束的时候,用ferror判断是读取失败结束,还是遇到文件末尾结束。
1.文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者是否为NULL(fgets)等。
每个函数有每个特定的结束标志。
2.二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
正确使用例子
#include <stdio.h> #include <stdlib.h> int main(void) { int c; // 注意:int,非char,要求处理EOF FILE* fp = fopen("test.txt", "r"); //如果为0,则打开失败 if(!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环 { putchar(c); } //feof是用来在结束后判断是什么原因结束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
文件缓冲区
在ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动的在在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
举例:
C语言代码也就是用户程序,要在屏幕上打印信息。则需要调用printf函数,而printf函数则调用了系统的API,让操作系统在屏幕上打印信息。但操作系统要为好多程序服务。所以在操作系统解决前,先放到文件缓冲区,程序攒满了再交给操作系统解决。
结论:
因为有缓冲区的存在,C语言再操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。
如果不做,可能导致读写文件的问题