C语言进阶 | 您所在的位置:网站首页 › 睿信是什么软件 › C语言进阶 |
目录 文件的随机读写 函数fseek 效果如下: 画图分析: 效果如下: 函数ftell 画图分析: 效果如下: 函数rewind 效果如下: 文本文件和二进制文件 简单介绍: 二进制文件: 文件读取结束的判定 被错误使用的feof 利用fgetc的返回值来判断循环是否结束: 效果如下: 若是想要判断是因为什么结束的 ,以下是文本文件的例子 效果如下: 二进制文件的例子: 效果如下: 文件缓冲区 在低版本vs可以用fflush VS2022 WIN11环境下测试的一段代码 效果如下: 下面我们给出一个解决方案 在VS2022 WIN11环境下,使用rewind(stdin)可以解决问题,效果如下: 如果深入了解scanf的用法也可以解决 文件的随机读写注明:点击函数可以跳转cplusplus网站!!! 函数fseek根据文件指针的位置和偏移量来定位文件指针。 int fseek ( FILE * stream, long int offset, int origin ); #include int main() { FILE* pf = fopen("text.txt", "r "); if (pf == NULL) { perror(pf); return; } //读文件 //fgetc返回指定流的内部文件位置指示符当前指向的字符,然后,内部文件位置指示器将前进到下一个字符。 int ch = fgetc(pf); printf("%c", ch);//a ch = fgetc(pf); printf("%c", ch);//b ch = fgetc(pf); printf("%c", ch);//c ch = fgetc(pf); printf("%c", ch);//d ch = fgetc(pf); printf("%c", ch);//e //关闭文件 fclose(pf); pf = NULL; return 0; }
效果如下: 现在指示器已经在f的位置,如果我们想要打印c,而不是按顺序读下去。那么就要用fseek
#include int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { perror(pf); return; } //读文件 //fgetc返回指定流的内部文件位置指示符当前指向的字符,然后,内部文件位置指示器将前进到下一个字符。 int ch = fgetc(pf); printf("%c", ch);//a ch = fgetc(pf); printf("%c", ch);//b ch = fgetc(pf); printf("%c", ch);//c ch = fgetc(pf); printf("%c", ch);//d ch = fgetc(pf); printf("%c", ch);//e //fseek(pf, 2, SEEK_SET);//起始位置开始 fseek(pf, -3, SEEK_CUR);//当前位置开始 //fseek(pf, -5, SEEK_END);//末尾开始 ch = fgetc(pf); printf("%c", ch); //关闭文件 fclose(pf); pf = NULL; return 0; } 画图分析:
效果如下:
函数ftell 返回文件指针相对于起始位置的偏移量 long int ftell ( FILE * stream ); #include int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { perror(pf); return; } //读文件 //fgetc返回指定流的内部文件位置指示符当前指向的字符,然后,内部文件位置指示器将前进到下一个字符。 int ch = fgetc(pf); printf("%c", ch);//a ch = fgetc(pf); printf("%c", ch);//b ch = fgetc(pf); printf("%c", ch);//c ch = fgetc(pf); printf("%c", ch);//d ch = fgetc(pf); printf("%c", ch);//e int a=ftell(pf); printf("%d", a); //关闭文件 fclose(pf); pf = NULL; return 0; } 画图分析:
当前指向的是f,相对于起始位置的偏移量为5 效果如下:
函数rewind 让文件指针的位置回到文件的起始位置 。 void rewind ( FILE * stream ); #include int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { perror(pf); return; } //读文件 //fgetc返回指定流的内部文件位置指示符当前指向的字符,然后,内部文件位置指示器将前进到下一个字符。 int ch = fgetc(pf); printf("%c", ch);//a ch = fgetc(pf); printf("%c", ch);//b ch = fgetc(pf); printf("%c", ch);//c ch = fgetc(pf); printf("%c", ch);//d ch = fgetc(pf); printf("%c", ch);//e //用rewind返回起始位置 rewind(pf); int a=ftell(pf); printf("%d\n", a); ch = fgetc(pf); printf("%c", ch);//打印的应该是a //关闭文件 fclose(pf); pf = NULL; return 0; } 效果如下: 文本文件和二进制文件 简单介绍: 根据数据的组织形式,数据文件被称为文本文件或者二进制文件。 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。 以ASCII字符的形式存储的文件就是文本文件。 一个数据在内存中是怎么存储的呢? 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而 二进制形式输出,则在磁盘上只占4个字节(VS2013测试)。 如有整数1,如果以ASCII码的形式输出到磁盘,则磁盘中占用1个字节(每个字符一个字节),而 二进制形式输出,则在磁盘上需要4个字节(VS2013测试)。 所以并不是说二进制的形式更省空间,而是要根据情况判断 二进制文件:我们以二进制形式将一个数写入文档 #include int main() { FILE* pf = fopen("test1.txt", "wb"); int num = 10000; fwrite(&num, 4, 1, pf); return 0; }可以发现,写入的数据我们是看不懂的
这里我们给一种查看的方式 ,流程如下: 点击现有项并添加刚刚创建的文件
点击打开方式: 选择二进制编辑器:
画图分析: 文件读取结束的判定 被错误使用的feof牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。 而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。 1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如: fgetc 判断是否为 EOF . fgets 判断返回值是否为 NULL . 2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。 例如 : fread判断返回值是否小于实际要读的个数。 fgetc函数的返回值分析 : 读取失败返回EOF 1.遇到文件末尾,返回EOF,同时设置一个状态,遇到了文件末尾,使用feof来检测这个状态 2.遇到错误,返回EOF,同时设置一个状态,遇到了错误,使用ferror来检查这个状态 利用fgetc的返回值来判断循环是否结束: #include int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { return 1; } int a = 0; while ((a = fgetc(pf)) != EOF) { printf("%c ", a); } fclose(pf); pf = NULL; return 0; } 效果如下: 若是想要判断是因为什么结束的 ,以下是文本文件的例子 #include int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { return 1; } int a = 0; while ((a = fgetc(pf)) != EOF) { printf("%c ", a); //putchar(a); } //判断是什么原因结束的 if (ferror(pf)) { puts("I/O error when reading"); } else if (feof(pf)) { puts("I/O error when reading"); } fclose(pf); pf = NULL; return 0; } 效果如下: 二进制文件的例子: #include enum { SIZE = 5 }; int main(void) { double a[SIZE] = { 1.0,2.0,3.0,4.0,5.0 }; 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 { if (feof(fp)) printf("Error reading test.bin: unexpected end of file\n"); else if (ferror(fp)) { perror("Error reading test.bin"); } } fclose(fp); } 效果如下:
文件缓冲区 ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓 冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根 据C编译系统决定的。 在低版本vs可以用fflush #include #include //VS2013 WIN10环境测试 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; } VS2022 WIN11环境下测试的一段代码 #include int main(void) { //VS2022 WIN11环境 int a, b; while (1) { a = scanf("%d", &b); printf("%d\n", a); fflush(stdin); } return 0; } 读取时输入缓冲区的内容会被scanf函数逐个取走,在正常情况下,scanf函数可以根据返回值判断成功取走的个数。若是应该读取一个整型,但是缓冲区里是一个字符,那么就会发生异常读取,而fflush函数并不支持高版本vs,缓冲区不会刷新,由于缓冲区的内容并未被取走,那么下次循环,scanf函数发现缓冲区有内容,不会等使用者输入内容,会再一次读取,发生异常读取,如此反复。 效果如下:下面我们给出一个解决方案 #include int main(void) { int a, b; while (1) { a= scanf("%d", &b); printf("%d\n", a); //fflush(stdin); rewind(stdin); } return 0; } 在VS2022 WIN11环境下,使用rewind(stdin)可以解决问题,效果如下: 如果深入了解scanf的用法也可以解决 链接1:C 清空输入缓冲区,以及fflush(stdin)的使用误区和解决方法_fflush(stdin)换行错误_hjtcn的博客-CSDN博客 链接2: https://zhidao.baidu.com/question/191469857.html?fr=qrl&cid=866&index=1&fr2=query 感兴趣的同学可以了解一下
|
CopyRight 2018-2019 实验室设备网 版权所有 |