四、文本文件和二进制文件
根据数据的组织形式,我们将数据文件分为文本文件和二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,那就是二进制文件,如果我们将数据的形式转换为ascll码的话,那就是文本文件
值得注意的是,字符在内存中均以ascll码的形式存储,数值型数据既可以用ascll形式存储,也可以用二进制形式存储
当用ascll形式存储时,我们会将10000看成5个字符,我们将这5个字符所对应的ascll码值,存储到内存里面
当用二进制形式存储时,10000其实就是个整型,我们将它按照4字节32比特位存储即可
五、文件的随机读写
前面给大家介绍的fgetc,fputc,fgets,fputs,fscanf,fprintf,fread,fwrite等操作文件的函数,其返回的指针是有统一顺序的,他的文件指针都是指向文件内容的第一个信息数据的地址的。
这里我们给大家介绍一下,能够改变文件指针指向位置的三个函数
5.1 fseek
int main() { FILE* pf = fopen("test.txt", "r");//以只读的方式打开文件test.txt if (pf == NULL) { printf("%s\n", strerror(errno)); } //1.定位文件指针 fseek(pf, -2, SEEK_END); //2.读取文件 int ch = fgetc(pf);//从pf指向的文件中读取内容 printf("%c", ch); //3.关闭文件 fclose(pf); pf = NULL; return 0; }
这里的fseek可以调整指针位置,以当前位置为起点,移动特定的偏移量到我们想要的位置
5.2 ftell
函数具体功能实现:
int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); } int ch = fgetc(pf);//从pf指向的文件中读取第一个字符,相应的指针位置相对于起始量偏移了1 int h = fgetc(pf);//偏移了2 int pos = ftell(pf);//所以这里的ftell返回值应该是2 printf("%d\n", pos);//结果应为2 return 0; }
我们这里的ftell函数作用起始就是返回当前文件指针相对于起始位置的偏移量
5.3 rewind
函数功能实现:
int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); } int ch = fgetc(pf); int h = fgetc(pf); int pos = ftell(pf); printf("%d\n", pos);//结果为2 rewind(pf);//我们这里让文件位置重新回到起始位置 pos = ftell(pf);//然后再用ftell返回当前文件指针的偏移量大小 printf("%d\n", pos);//返回到起始位置后,结果为0 return 0; }
所以这个函数功能也是比较简单的,就是重置我们的文件指针指向的位置,让他回到起始位置
六、文件读取结束的判定
6.1 feof的错误使用
Tests for end-of-file on a stream.这个是feof的函数介绍,我们可以知道,这个函数是用来测试文件结束形式的。所以这个函数不是用来判断文件是否结束的,而是用来判定文件是如何结束的,到底是因为读取到\0结束的?还是因为其他原因导致文件读取错误,而导致文件结束的?
所以很多人看到feof(end of file)时会把他认为成一个判断文件是否结束的函数,但其实不是这样的The feof function returns a nonzero value after the first read operation that attempts to read past the end of the file. It returns 0 if the current position is not end of file. There is no error return.通过这里的feof函数的返回值介绍(如果当前位置不是文件末尾则返回一个0(有可能发生了读取文件错误),如果成功读取结束的话,将返回一个非0值),我们就可以明白了,这个函数的确是用来测试文件读取结束的方式的,既有可能是读取错误导致的结束,也有可能是读到文件末尾,从而导致的读取结束
6.2 ferror、perror、strerror的对比
perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 错误 (stderr) 。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno 的值来决定要输出的字符串。
在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了error的值。perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出。
1.ferror(fault error)的功能: 测试流上的错误。如果流上没有发生错误,ferror返回0。否则,它返回一个非零值。
2.perror的功能: 直接打印错误信息(里面包含我们所输入的信息和错误码所对应的信息一并打印出来)
3.strerror的功能: 把错误码对应的错误信息的字符串地址返回(配合errno使用,errno是一个全局变量,当出现错误时,errno会对应一个库中错误信息对应的错误码,然后我们再用strerror打印这个错误码对应的错误信息)
6.3 判断文件结束的两个例子
6.3.1 文本文件的判断
文本文件的例子: int main(void) { int c; // 注意:int,非char,要求处理EOF FILE* fp = fopen("test.txt", "r"); if (!fp) //fp是空指针的时候,进入到if语句,输出错误信息 { perror("File opening failed"); return ; } //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环 { //这里循环的条件就是,读取的字符不是EOF,一直将字符输出 putchar(c); } //文件读取之后结束了,然后判断是什么原因结束的 if (ferror(fp))//发生错误,返回一个非0值,进入if语句,打印读取失败的信息 puts("I/O error when reading"); //在读取文件时,input或output发生错误 else if (feof(fp)) //如果函数feof返回非0值,说明是遇到了EOF结束的,如果当前的位置不是文件末尾,则返回0,也就不是遇到EOF puts("End of file reached successfully"); fclose(fp); }
自己实现相应的代码,加深理解 int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { return 0; } //读文件 int ch = 0; while ((ch = fgetc(pf)) != EOF) { putchar(ch); } printf("\n"); //跳出循环,判断是怎么结束的??? if (ferror(pf))//是读取失败结束??? { printf("error\n"); } else if (feof(pf))//还是遇到文件尾结束??? { printf("end of file\n"); } fclose(pf); pf = NULL; return 0; } 总结:所以ferror和feof要配合使用,以此来判断是读取失败结束还是遇到文件尾结束 ferror如果检测没有错误返回0 有错误返回非0值 feof如果没到文件尾的话他会返回一个0,到达文件尾返回一个非0值
6.3.2二进制文件的判断
//二进制文件例子: #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 的数组到pf流里面去 fclose(fp);//然后关闭文件 double b[SIZE]; fp = fopen("test.bin", "rb"); size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 将从fp流中读取的数据放到double数组b里面(以二进制的形式读出来) if (ret_code == SIZE) //如果返回值等于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)) //(到达文件尾返回一个非0值进入if语句,但它并非我是所预取的文件结束位置,虽然我的预期有可能是错误的,但你返回值<SIZE,我认为这就是unexpected的) { printf("Error reading test.bin: unexpected end of file\n"); //提前阅读文件结束,导致返回值小于SIZE,这时unexpected end of file非预期文件结束 } else if (ferror(fp)) { perror("Error reading test.bin");//读取错误导致文件读取结束 } } fclose(fp); }
6.4文本文件和二进制文件的对比
(1)文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)
例如:
fgetc判断是否为EOF(fgetc返回读取为int的字符,或返回EOF以指示错误或文件结束)
fgets判断是否为NULL(返回NULL表示错误或文件结束条件,使用feof或ferror来确定是否发生了错误)
(2)二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
例如:fread判断返回值是否小于实际要读的个数
(Fread返回实际读取的完整项数,如果发生错误或在达到count之前遇到文件结束,则该数可能小于count)