fwrite / fread 返回值的陷阱
fwrite / fread函数返回值的陷阱
二进制文件没有\n / EOF / len-1 作为读出的结束标识,fread 依靠读出块多少来标识读结果和文件结束标志。
以最小的单元格式进行读,或是以写入的最小单元进行读
我们通过文本进行写入:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwirte((void *)buf,1,8,pf);
fclose(pf);
return 0;
}
执行结果为:
我们可以看到写入成功。
如果我们换一种,让块的大小为8一次性写入。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
fclose(pf);
return 0;
}
执行结果为:
也没有问题。
但是在读的时候由很大的区别。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
rewind(pf);
char read[10];
int n;
n = fread((void *)read,1,8,pf); //n表示读取到完整块的个数
printf("n = %d\n",n);
n = fread((void*)read, 1,8, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
fclose(pf);
return 0;
}
执行结果为:
我们可以看到,第一个n返回为8,一共读取了8次,每一次块的大小为1,所以返回的n完整的块个数为8个。当读取8块之后,文件位置到了结束位置,所以再读取的时候为0。
我们再把块的大小设置为8:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
rewind(pf);
char read[10];
int n;
n = fread((void *)read,8,1,pf); //n表示读取到完整块的个数
printf("n = %d\n",n);
n = fread((void*)read, 8,1, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
fclose(pf);
return 0;
}
执行结果为:
我们看到一次8个字符全部被读取,所以刚好一个完整的块读取完。
那如果我再把块的大小设置为7:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
rewind(pf);
char read[10];
int n;
n = fread((void*)read, 7, 1, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
n = fread((void*)read, 7, 1, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
fclose(pf);
return 0;
}
执行结果为:
这就意味这第一次读取到了1234567,没有读取完,所以下一次再读取的时候还需要读7个。这里肯定是读取两次,但是第二次的返回结果为0,但是在一般情况下返回值只有在大于0的时候我们才关系,等于0的时候表示结束。
我们再把块的大小设置为3,我们现在让返回值打印三次,因为两次只能读取6个字节:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
rewind(pf);
char read[10];
int n;
printf("n = %d\n", n);
n = fread((void*)read, 3, 1, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
n = fread((void*)read, 3, 1, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
fclose(pf);
return 0;
}
执行结果为:
我们可以看到前两次返回值为1,总共读取了6个字节,没有读取完需要再读取一次,返回值为0,因为6个字节读取完之后剩下两个字节,两个字节不能构成块。
所以我们在这只的时候要把块的大小设置为单元的块,我们这里一个字节设置一个块, 然后每次读取3个就没有问题。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
rewind(pf);
char read[10];
int n;
n = fread((void*)read, 1, 3, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
n = fread((void*)read, 1, 3, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
n = fread((void*)read, 1, 3, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
n = fread((void*)read, 1, 3, pf); //n表示读取到完整块的个数
printf("n = %d\n", n);
fclose(pf);
return 0;
}
我们打印4次,执行结果为:
我们可以看到读取到了8个字节,后面再读取的时候为空。
那我们就可以把打印过程写成循环形式:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char buf[1024] = {"12345678"};
FILE* pf= fopen("oo.txt","w+");
if (pf == NULL)
exit(-1);
fwrite((void *)buf,8,1,pf);
rewind(pf);
char read[10];
int n;
while ((n = fread((void*)read, 1, 3, pf)) > 0)
{
for (int i = 0; i < n; i++)
{
printf("%c",read[i]);
}
}
fclose(pf);
return 0;
}
执行结果为:
文件里面的数据全部读取并打印出来。
我们说以块的大小问单元但是并不是说都是以1个字节,上面的代码由于我们写进去的数据为char类型,所以在读取的时候以字节为单元。如果我们定义int类型数据那么就以int类型4个字节为单元进行数据读取。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf = fopen("qq.txt","w+");
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
fwrite((int*)arr,10,4,pf);
int n = 0;
rewind(pf);
while ((n = fread((int*)arr, 4, 10, pf)) > 0)
{
for (int i = 0; i < n; i++)
{
printf("%d\t",arr[i]);
}
}
return 0;
}
执行结果为:
小结
至此我们已经把fwrite / fread 返回值的陷阱已经详细说明,过程比较详细,读者可以多次阅读来不断理解。