3.5 fscanf(从文件中读取数据)
int fscanf ( FILE * stream, const char * format, ... );
1、这个函数和scanf:(int scanf (const char* format,...);)是非常相似的,只不过这个函数多了一项从流(stream)中获取数据,后面基本相同的。如果想让这个函数发挥和printf一样的功能,那么只需将流改为 stdin(标准输入).
2、而且这个函数不仅仅可以读取字符串,对于其他类型的整型,结构体,浮点型等都是可以读取的。
3、如果成功,则返回成功读取的项数,如果失败,则返回EOF。
应用展示:
3.6 fprintf(写数据到文件)
int fprintf ( FILE * stream, const char * format, ... );
1、同理,这个函数和printf(int printf(const char* format,...)),非常相似,这个函数多了一项向流(stream)中写入数据,后面基本相同的。如果想让这个函数发挥和printf一样的功能,那么只需将流改为 stdout(标准输出).
2、这个函数也是不仅仅可以写入字符串,对于其他类型的整型,结构体,浮点型等都是可以写入的。
3、如果成功,则返回成功写入的项数,如果失败,则返回EOF。
应用展示:
阐述:通过上面对fscanf、fprintf的介绍,我们知道了标准输入流(stdin)、标准输出流(stdout),那么为什么在使用printf和scanf的时候不需要打开那两个流呢?
因为任何一个C程序,只要运行起来就会默认打开3个流:
FILE* stdin---标准输入流(键盘) FILE* stdout-- 标准输出流(屏幕) FILE* stderr--标准错误流(屏幕)
上面介绍的fputc,fgetc只能一个个写入或读取字符,而fputs,fgets也只能写入或读取字符串,fprintf和fscanf可以写入或读取任意类型的数据,但是必须设置格式,而且一次只能读取或写入一个。如果我们既想写入或读取任意类型的数据,还不想要设置格式的繁琐,还想要一次可以写入或读取多个数据,上面的几个文件操作函数是无法满足这样的条件的,所以接下来我要介绍的fwrite和fread可以满足这些需求。
注意:fwrite和fread与上述文件操作函数的区别还在于,它们只能以二进制的形式写入或读取二进制文件。
3.7 fwrite(可写多个数据到文件)
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
1、意义:从 ptr 指向的当前位置将count个大小为size(字节)的数据写入流(stream)。
2、ptr是要写入的数据,size是指定一次写入多少个字节,count意思是写入count次size个字节。
3、返回成功写入的字节总数。如果此数字与 count 参数不同,则会出现写入错误,导致函数无法完成。
应用展示:
3.8 fread(可从文件读取多个数据)
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
1、意义:从流stream中读取 count个大小为size个字节的数据到ptr中。
2、ptr是读取成功后存放数据的地址,size是每次读取字节的大小,count是读取多少次。
3、如果成功,返回的是读取的总字节数为(大小*计数)。
应用展示:
3.9 sscanf 和sprintf
这两个函数比较有意思
1、
sscanf -- int sscanf( const char *buffer, const char *format,... );
从字符串里读并将其转化为格式化数据。从 buffer 读取数据,并根据参数格式将它们存储到附加参数给出的位置,就像使用 scanf 一样,但从 buffer 而不是标准输入 (stdin) 读取。
返回值:成功则返回成功读取的项目数,如果失败,则返回EOF。
2、
sprintf --int sprintf( char *buffer, const char *format,...);
把一组格式化的数据写到字符串中,本质是把一组格式化数据转换成字符串。
它的使用与 printf 相似,但内容不是打印,而是将格式化数据转换成字符串存储在buffer 中。终止空字符会自动追加到内容之后。
返回值:成功后,将返回写入的字符总数。此计数不包括自动追加在字符串末尾的其他空字符。失败时,返回负数。
使用展示:
四、文件的随机读写
4.1 fseek(重定位文件指针的位置)
int fseek ( FILE * stream, long int offset, int origin );
1、作用:根据文件指针的位置和偏移量来定位文件指针,这样说可能比较抽象,编译器默认从初始位置开始读取文件,但是这个函数可以重新定位文件指针。
2、stream是要重新定位的文件,offset是要偏移的量,origin是从哪里开始偏移。
origin的选取:
origin的选取有三个:
SEEK_SET
,文件的起始位置; SEEK_CUR
,文件指针的当前位置,SEEK_END
,文件尾
3、成功则返回0,失败则返回非0的数。
4.2 ftell(偏移量是多少)
long int ftell ( FILE * stream );
作用:计算文件指针当前位置偏移量。
返回值:成功则返回当前偏移量。
4.3 rewind(回到起点)
void rewind ( FILE * stream ); 作用:将指针的位置设为开头。
结合应用展示:
int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); return 1; } //读文件 //定位文件指针 fseek(pf, 2, SEEK_SET); int ch = fgetc(pf); printf("%c\n", ch); //偏移量要看定位处 //int fseek(FILE * stream, long offset, int origin); //fseek(pf, 2, SEEK_CUR); fseek(pf, -1, SEEK_END); ch = fgetc(pf); printf("%c\n", ch); //ftell //返回文件指针相对于起始位置的偏移量 //ftell用来计算文件指针相对于当前位置的偏移量 //long int ftell(FILE* stream) printf("%d\n", ftell(pf)); //rewind rewind(pf);//让文件指针回到起始位置 ch = fgetc(pf); printf("%c\n", ch); //关闭文件 fclose(pf); pf = NULL; return 0; }
五、二进制文件和文本文件
1、数据文件被称为文本文件或者二进制文件
2、数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式
存储的文件就是文本文件。
3、一个数据在内存中是怎样存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节,每个字符一个字节,而以二进制形式输出则在磁盘上只占4个字节.
六、文件读取结束的判定
6.1 被错误使用的feof
int feof ( FILE * stream );
在文件读取的过程中,不能用feof函数的返回值直接用来判断文件的是否结束,这个函数使用的前提是已经设置了与流关联的文件结束指示符,如果设置了,则返回与零不同的值。也就是成功。如果没有设置,就会返回0.
通俗的讲,这个函数是为了判断是不是因为没有设置结束符而导致失败。如果设置了就排除这一可能。
文本文件使用案例:
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL
int main() { int c;//注意:int,非char,要求处理EOF FILE* fp = fopen("test.txt", "r"); if (!fp) { perror("File opening failed"); return 1; } //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF while ((c = fgetc(fp) != EOF))//标准C I/O读取文件循环 { printf("%c ", c); } if (ferror(fp)) { puts("I/O error when reading"); } else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); return 0; }
二进制文件使用案例:
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数
enum {SIZE=5}; int main() { 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); fp = NULL; }
七.文件缓冲区
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
fflush:
int fflush ( FILE * stream );
用途:如果给定的流(stream)已打开以进行写入,则其输出缓冲区中的任何未写入数据都将写入该文件。
简单来说就是清理缓冲区上未写入文件的内容,将这些输出到文件。
返回值:0表示成功,EOF表示失败。
效果展示:
注意:如果缓冲区没有填满,也没有刷新缓冲区,那么在文件操作结束的时候,缓冲区的s数据会自动输出到文件中。