C文件操作之fread,fwrite函数

 格式:
    int fread(void *ptr, int size, int nitems, FILE *fp)
 参 数:
      用于接收数据的地址(字符型指针)(ptr)
  单个元素的大小(size)
  元素个数(nitems)
  提供数据的文件指针(fp)
  返回值:成功读取的元素个数

   格式:fwrite(buffer,size,count,fp);
  (1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
  (2)size:要写入的字节数;
  (3)count:要进行写入size字节的数据项的个数;
  (4)fp:目标文件指针。
  说明:写入到文件的哪里? 这个与文件的打开模式有关,如果是r+,则是从file pointer指向的地址开始写,替换掉之后的内容,文件的长度可以不变;如果是a+,则从文件的末尾开始添加,文件长度加大,而且fseek函数对此函数没有作用。

  程序例:
  #include <string.h>
  #include <stdio.h>
  int main(void)
  {
   FILE *fp;
   char msg[] = "this is a test";
   char buf[20];
   if (NULL == (fp = fopen("DUMMY.FIL", "w+")))
   {
    fprintf(stderr, "Cannot open output file./n");
    return 1;
   }   
   fwrite(msg, strlen(msg)+1, 1, fp); /* write some data to the file */
   
   fseek(fp, 0, SEEK_SET); /* seek to the beginning of the file */
  
   fread(buf, strlen(msg)+1, 1,fp); /* read the data and display it */
   printf("%s/n", buf);
 
   fclose(fp);
   return 0;
  }

问题(来自百度):

fread大概只能读10k
从一个文件中把数据复制到另外一个文件中,我的代码如下:
while (i<38016)//文件大小为38016字节,i(unsigned int),temp(char)
{
 fread(&temp,1,1,fyuv);
 fwrite(&temp,1,1,fyuv1);
 i++;
}
结果文件有38k,其中前面大概10k是正确的(与源文件进行二进制代码比对),后面的约28k就重复前面10k的最后一个字节,
也就是后面28k所有字节完全一样。 若把代码换成:
do
{
 i=fread(&temp,1,1,fyuv);
 fwrite(&temp,i,1,fyuv1);
}while(i>0);
则得到的结果文件大概只有10k,且这10k与源文件的前10k一样(比对二进制代码),但是后28k就没有了。
请问我该怎么做才是正确的呢?
ps:如果用CFile类则可以正确地复制文件,但是我现在想用fread函数来实现。
错误原因:
你用文本方式打开了二进制文件
文本方式读取二进制数据, 可能在文件结束之前将某段数据判定为文件末尾EOF, 所以结束读取( 举个例子, 比如遇到 0x00 0x00 0xff 0xff, 则文本方式方式的文件流, 认为已经到文件末尾, 不能读取)

你这个38016的文件, 大概在10k左右有段数据和文件结束标志格式相同, 文本方式读取到10k左右就认为文件结束了( 真正的文本文件, 结束标志可能在磁盘簇的剩余空间中 )

所以第一种方式:
固定读取38016次, 每次往新文件中写一个字节; 前10k次能读取到内容, fread返回值是1, 这样写过去的一字节就是读取的字节; 后28k因为读取失败, fread返回值为0, 这样temp的内容就不会被改写, 仍然是最后一次成功读取的值, 但因为是写次数固定, 所以后28k就重复写过去;
后一种方式:
根据fread的返回值来判定文件结束, 这是正确的方法; 所以读取到10k后, 返回值为0, 表示无效, 文件结束, 所以只复制了10k内容

CFile只支持二进读写, 所以你的结果是正确的( CFile用CFile::typeText格式会报错; CStdioFile才能文本读写)
用fopen返回的FILE, 如果读取的时候没有加b( 比如"r"), 则默认的是文本格式; 所以请用"rb"来读取二进制文件,
用"wb"写二进制文件; 当然如果只是复制文件的话, 纯二进制读写没有问题

下面是楼主要的效果, 是一个字节读写的
#include <stdio.h>
int main()
{
 FILE *pFileS = fopen("s.rar", "rb");
 if(!pFileS)
  return 1;
 FILE *pFileD = fopen("d.rar", "wb+"); 
 unsigned char bTemp;
 while(fread(&bTemp, sizeof(unsigned char), 1, pFileS))
 fwrite(&bTemp, sizeof(unsigned char), 1, pFileD );
 fclose(pFileS), fclose(pFileD);
 return 0;
}
其实一个字节读写的话, 用fgetc和fputc就可以了, 当然还是得以二进制方式打开

另外单字节读写速度太慢; 系统中复制文件都是整块读写的, 设置缓冲大小
比如
#include <stdio.h>
int main()
{
FILE *pFileS = fopen( "s.rar", "rb" );
if(!pFileS )
return 1;
FILE *pFileD = fopen( "d.rar", "wb" );
unsigned char buffer[ 4 * 1024 ];
int nRead;
while( nRead = fread( buffer, sizeof(unsigned char), sizeof(buffer), pFileS ) )
fwrite( buffer, sizeof(unsigned char), nRead, pFileD );
fclose( pFileS ), fclose( pFileD );
return 0;
}

另外, fread单次读取的总字节数有限制, 也就是说缓冲有上限; 只能通过提高次数来读取大文件; 在这方面, 用API如ReadFile或者调用了这些API的封装类就好得多; 当然, 次数多对电脑来说不是问题呵呵

附:
至于文本方式不能完全读取, 而二进制方式能的原因-
文本方式读取文件, 最主要的用处是一次读取一整句( 以换行符'/n', 即二进制的换行标志"/r/n"结束 ), 方便用于特殊用处ReadString、fscanf(...,"%s",...)之类, 每次读取的内容长度是不定的; 而二进制读取方式Read、fread等, 都是读取固定长度
所以文本方式读取对EOF的判定, 是一个文件尾结束标志, 如果是文本文件, 则这个文件尾肯定不会出现在文件内容中( 因为是不可打印字符构成的结束标志, 人可读的文本文件不会包括它 ), 这样以结束标志为文件尾则是可以的; 二进制文件内容可以是任意字节, 如果把它当文本文件来读, 以文件尾为结束, 当然可能出现把文件内容判定为文件尾的情况;
二进制读取方式由于每次读取固定字节, 所以只需要用总文件长度( 这个数值是系统管理的数值, 不是计算得出来的 )减去每次读取的长度( 或根据Seek的位置计算长度 ), 就可以知道是否到文件尾, 不需要定义结束标志; 所以用二进制方式打开任何文件都是合理的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值