本文继续上一篇文件操作内容并继续其编号。
十四、函数 fwrite()
fwrite()一般用来将数组等较大的数据块写入文件,写入方式尽量使用二进制方式,若以非二进制方式写入数据,容易发生"不易查找"的错误,该函数原型定义在 stdio.h中。
使用格式:fwrite(参1, 参2, 参3, 参4);
参1:数组指针(这个参数的类型是无类型指针<void*>,所以可接受任何类型的指针)
参2:每个数组成员的字节数(可用sizeof()获得)
参3:数组成员的个数
参4:要写入的文件指针
返回值:函数的返回值是写入的数组成员的个数(不是字节数),运行正常返回值就是参3,返回值类型是size_t。
说明:
a.一个数组如果有100个成员,那么只写入50个、80个都是可以的,不一定全部写入文件;
b.非数组类型的数据,如我们自定义的struct person结构体对象,也可以看成是只有一个成员的数组写入文件,无非是参3等于1。
c.如以二进制方式向文件写入数据,类似字符串结束符"\0"等,都会被写入文件。所以,读取、解析这些数据时,要考虑这些与文本文件不一致的地方。
十五、函数fread()
前边有函数fwrite写入,后边必然跟着函数fread读取。该函数用于从文件读取数组等较大的数据块,读取方式应尽量采用二进制方式,若以非二进制方式读取数据容易发生错误。该函数原型定义在头文件stdio.h中 。
使用格式:fread(参1, 参2, 参3, 参4);
参1:数组地址(这个数组是准备接受读取到的数据的)
参2:单个数组成员的字节数
参3:数组成员个数
参4:要读取数据的文件指针
返回值:函数返回读取的数组成员的个数,正常应当是参3,出现问题会少,一个没读取成功是0。
说明:
a.与fwrite()相比,除了一个是读一个是写的差别外,其它方面一样。
b.读取的原则:当时怎样写入的,就怎样读取,必须保证一一对应才能正确解析出存入的内容。
c.如存入时存入了诸如"\0"、"\n"之类的可能影响指针移动的字符时,解析时要想着到这个地方要跳过去。
十六、函数feof()
函数feof()用于判断文件的内部指针是否到达文件结尾,该函数原型定义在头文件stdio.h中。
使用格式:feof(参1);
参1:文件指针
返回值:如果到达文件结尾,返回一个非零值(true),否则返回0(false)。
说明:
a.有些读取函数遇到EOF会停止读取,发生错误也会停止读取,是否读取完毕,可以通过这个函数予以确认。
十七、函数fseek()
fseek函数:f文件之意,seek查找之意,合起来可以理解成在文件中定位。打开文件会有一个指向该文件的文件指针,而文件指针内部还有一个指针我们称之为内部指针,用来记录被打开文件的读写位置,即再次调用函数进行读写的开始位置。可以循环使用的文件操作函数一次次读取数据都是通过这个内部指针进行定位的。要操作这个内部指针,fseek()是可用函数之一。这个函数的原型定义在头文件stdio.h中。
使用格式:fseek(参1, 参2, 参3);
参1:文件指针
参2:偏移量(单位:字节 类型:long,如使用负值表示向回移动)
参3:起点位置(有三个值:SEEK_SET—文件开始、SEEK_CUR—当前位置、SEEK_END——文件结尾)
返回值:运行正常情况下,fseek的返回值为0。如果发生错误返回值为非零值。
说明:
a.参2为long类型,如写一个常量,要加上"L"修饰,如200L。
b.函数fseek()最好用来操作二进制文件;如用来读取文本文件,要掌握文本的编码方式,否则容易把一个整体数据(如一个汉字)断成两截。
c.偏移量不论是正值还是为负值,都应保证在SEEK_SET、SEEK_END之间,不要溢出。
十八、使用上述函数的举例程序如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
//实验fwrite()函数向文件temp.dat写入数据
int arr[5] = { 31,22,43,54,75 };
FILE* fp=fopen("temp.dat","wb");//二进制写方式打开文件
fwrite(arr, sizeof(arr[0]), sizeof(arr)/sizeof(arr[0]), fp);
//arr—数组名(数组指针)
//sizeof(arr[0])—单个成员字节数
//sizeof(arr)/sizeof(arr[0])—数组成员个数
fclose(fp);
//实验fread()函数读取temp.dat文件中数据
if (NULL == freopen("temp.dat", "rb", fp)) return 0;
int arr2[5]; //用于接收读取数据的数组
fread(arr2, sizeof(int), 5, fp);
for (int i = 0; i < 5; i++)
{
printf("数组成员%d=%d\n", i, arr2[i]);
}
fclose(fp);
//实验fseek函数
if (NULL == freopen("temp.txt", "w+", fp)) return 0;
//以二进制写读方式重新打开temp.dat文件,内容被清空
char str[] = "abcdefghijklmnopqrstuvwxyz";
fwrite(str, sizeof(char), 26, fp); //将26个小写字母写入文件
//提示:对于w+这种写读一体的模式,完成写操作后,立即进行读操作,尽管此时
//内部指针指着最后一个字母,但实际读不出内容来。如要转入读操作,必须重置
//内部指针。
fseek(fp, 0L, SEEK_SET);
//将内部指针置于起始位置,即字母a处(注意:结尾不是z处,而是EOF)
printf("%c\n",getc(fp)); //运行结果:a
fseek(fp, -(long)1*sizeof(char), SEEK_END);
//将内部指针置于文件结尾位置,并向回偏移1*sizeof(char)字节
//偏移量使用sizeof()不易出差错
printf("%c\n",getc(fp)); //运行结果:z
fseek(fp, -(long)2 * sizeof(char), SEEK_CUR);
//前边把z读出了,读出z后指针自动向后走,指向了EOF;因此,这次的起点位值
//用的是当前位置(SEEK_CU),即当前位置就是EOF,从EOF开始,向回走2个位置,
//指针正好指向字母 y
printf("%c\n", getc(fp)); //运行结果:y
//实验feof函数
fseek(fp, 0L, SEEK_END);
printf("feof1=%d\n", feof(fp)); //运行结果:feof1=0
printf("%d\n",getc(fp)); //运行结果:-1(-1没有对应的字符,所以用%d输出)
printf("feof2=%d\n", feof(fp)); //运行结果:feof2=1
//分析:我们把内部指针置在EOF位置,这时我们用feof检查,返回值为0,也就是说feof()函数
//并不认为文件已经到达结尾;接着我们再用getc读了一次,读出的值是-1,证明确实在EOF位置;
//当读完了EOF后内部指针已经越过了EOF,这时我们再用feof检查,返回值是1,说明文件结束了。
//总结:feof()不是碰见EOF位置就返回到达文件结尾,而是指针得过了EOF才返回到达文件结尾。
fclose(fp);
getchar();
return 0;
}