[C][文件操作][二][文件的随机读写][文件读取结束的判定][文件缓冲区]详细讲解
目录
- 1.对比一组函数
- 2.文件的随机读写
- fseek()
- ftell()
- rewind()
- 3.本件和二进制文件
- 4.文件读取结束的判定
- 1.被错误使用的feof()
- 2.ferror()
- 5.文件缓冲区
1.对比一组函数
- scanf:针对标准输入的格式化的输入语句 - stdin
- fscanf:针对所有输入流的格式化的输入语句- stdin/FILE
- sscanf:从一个字符串中读取一个格式化的数据
- 函数原型:int sscanf(const char* str, const char* format, ...)
- 参数:
- str:是C字符串,是函数检索数据的源
- format:格式说明符,用法同scanf
- 返回值:
- 如果成功,该函数返回成功匹配和赋值的个数
- 如果到达文件末尾或发生读错误,则返回EOF
- printf:针对标准输出的格式化输出语句 - stdout
- fprintf:针对所有输出流的格式化输出语句 - stdout/FILE
- sprintf:把一个格式化的数据,转换成字符串
- 函数原型:int sprintf(char* str, const char* format, ...)
- 参数:
- str:指向一个字符数组的指针,该数组存储了 C 字符串
- format:格式说明符,用法同printf
- 返回值:
- 如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符
- 如果失败,则返回一个负数
- 例子:
struct S { char arr[10]; int age; float f; }; int main() { struct S s = { "hello", 20, 5.5f }; struct S tmpprintf = { 0 }; char buf[100] = { 0 }; // sprintf 将一个格式化的数据,转换成字符串 sprintf(buf, "%s %d %f", s.arr, s.age, s.f); printf("%s\n", buf); // 从buf字符串中还原出一个结构体数据 sscanf(buf, "%s %d %d", tmp.arr, &(tmp.age), &(tmp.f)); printf("%s %d %f\n", tmp.arr, tmp.age, tmp.f); return 0; }
2.文件的随机读写
fseek()
- 函数原型:int fseek(FILE* stream, long offset, int origin)
- 描述:将与流关联的位置指示器设置为新位置
- 参数:
- stream:这是指向FILE对象的指针,该FILE对象标识了流
- offset:这是相对origin的偏移量,以字节为单位
- origin:这是表示开始添加偏移offset的位置,它一般指定为下列常量之一
- SEEK_SET:文件的开头
- SEET_CUR:文件指针的当前位置
- SEEK_END:文件的末尾
- 返回值:如果成功,则该函数返回零,否则返回非零值
ftell()
- 函数原型:long ftell(FILE* stream)
- 功能:返回文件指针相对于起始位置的偏移量
- 参数:stream:这是指向FILE对象的指针,该FILE对象标识了流
- 返回值:
- 该函数返回位置标识符的当前值
- 如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值
rewind()
- 函数原型:void rewind(FILE* stream)
- 描述:让文件指针的位置回到文件的起始位置
- 参数:这是指向FILE对象的指针,该FILE对象标识了流
3.本件和二进制文件
- 根据数据的组织形式,数据文件被称为文本文件或者二进制文件
- 二进制文件:数据在内存中以二进制的形式存储,不加转换的输出到外存
- 文本文件:
- 在外存上以ASCII码的形式存储,则需要在存储前转换
- 以ASCII字符的形式存储的文件就是文本文件
- 一个数据在内存中是怎么存储的呢?
- 字符一律以ASCII形式存储
- 数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储
- 例子:有整数10000
- 如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节)
- 而二进制形式输出,则在磁盘上只占4个字节
4.文件读取结束的判定
1.被错误使用的feof()
-
返回值:当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零
-
牢记:在文件读取过程中,不能用feof()的返回值直接用来判断文件的是否结束
- feof()应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
- 文本文件读取是否结束,判断返回值是否为EOF,或者NULL
- fgetc()判断是否为EOF
- 在读取结束的时候,会返回EOF
- 正常读取的时候,返回的是读取到的字符的ASCII码值
- fgets()判断返回值是否为NULL
- 在读取结束的时候,会返回NULL
- 正常读取的时候,返回存放字符串的空间起始地址
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
- fread()判断返回值是否小于实际要读的个数
- 在读取的时候,返回的是实际读取到的完整元素的个数
- 如果发现读取到的完整的元素的个数小于指定的元素个数,这就是最后一次读取了
-
文本文件的例子
int c; // 注意:int,非char,要求处理EOF FILE* fp = fopen("SnowK.txt", "r"); if(!fp) { perror("File Opening Failed"); return EXIT_FAILURE; } // fgetc当读取失败的时候或者遇到文件结束的时候,都会返回EOF while((c = fgetc(fp)) != EOF) // 标准C IO读取文件循环 { putchar(c); } // 判断是什么原因结束的 if (ferror(fp)) // 文件读取失败结束 { puts("I/O error when reading"); } else if (feof(fp)) // 遇到文件结束表示,文件正常结束 { puts("End of file reached successfully"); } fclose(fp);
-
二进制文件的例子
enum { SIZE = 5 }; int main() { double a[SIZE] = {1.,2.,3.,4.,5.}; FILE *fp = fopen("SnowK.bin", "wb"); // 必须用二进制模式 fwrite(a, sizeof(*a), SIZE, fp); // 写double的数组 fclose(fp); double b[SIZE]; fp = fopen("SnowK.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
2.ferror()
- 函数原型:int ferror(FILE* stream)
- 返回值:如果设置了与流关联的错误标识符,该函数返回一个非零值,否则返回一个零值
5.文件缓冲区
-
ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”
- 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上
- 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)
- 缓冲区的大小根据C编译系统决定的
-
结论:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件
- 如果不做,可能导致读写文件的问题
FILE* pf = fopen("SnowK.txt", "w"); fputs("abcdef", pf); // 先将代码放在输出缓冲区 printf("睡眠10秒-已经写数据了,打开SnowK.txt文件,发现文件没有内容\n"); Sleep(10000); printf("刷新缓冲区\n"); fflush(pf); // 刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘) //注:fflush 在高版本的VS上不能使用了 printf("再睡眠10秒-此时,再次打开SnowK.txt文件,文件有内容了\n"); Sleep(10000); fclose(pf); //注:fclose在关闭文件的时候,也会刷新缓冲区 pf = NULL;
- 如果不做,可能导致读写文件的问题
-
- fread()判断返回值是否小于实际要读的个数
- fgetc()判断是否为EOF
- 文本文件读取是否结束,判断返回值是否为EOF,或者NULL
- feof()应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
-
- 根据数据的组织形式,数据文件被称为文本文件或者二进制文件
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。