中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > C/C++
我的程序里边,为什么fwrite没有把数据写到文件里边啊?
作者:未知 时间:2005-09-13 19:28 出处:ChinaUnix.net 责编:chinaitpower
              摘要:我的程序里边,为什么fwrite没有把数据写到文件里边啊?

[quote:0ddfa517d3]#include <stdio.h>
#include "err_exit.h"


struct record{
  int uid;
  char login[9];
};

char *logins[]={"user1","user2","user3","user4","user5"};

void putrec(FILE *fp,int i,struct record *r)
{
  fseek(fp,(long)i*sizeof(struct record),0);
  [color=red:0ddfa517d3]fwrite((char *)r,sizeof(struct record),1,fp);[/color:0ddfa517d3]
}

int main(void)
{
  int i;
  FILE *fp;
  struct record rec;
  
  if ((fp = fopen("datafile","w"))==NULL)
     err_exit("datafile");
     
   for(i=4;i>=0;i--)
      {
         rec.uid=i;
         strcpy(rec.login,logins[i]);
         putrec(fp,i,&rec);
       }
       fclose(fp);
       exit(0);
}
[/quote:0ddfa517d3]

执行该程序后datafile文件是空的啊

 fwizard 回复于:2004-04-28 11:15:24
whyglinux帮忙看一下啊,谢谢

 FH 回复于:2004-04-28 11:18:38
fwrite()返回码

 飞灰橙 回复于:2004-04-28 11:25:08
程序没有问题

 fwizard 回复于:2004-04-28 11:26:31
楼上的意思是说要看fwrite()返回什么内容吗?

 zkyguanl 回复于:2004-04-28 11:28:47
if ((fp = fopen("datafile","w"))==NULL) 
改成
if ((fp = fopen("datafile","wb"))==NULL)

 fwizard 回复于:2004-04-28 11:32:34
b是binary吧,还是不行啊
是不是系统兼容的问题,我的系统是solaris9

 zkyguanl 回复于:2004-04-28 11:38:18
void putrec(FILE *fp,int i,struct record *r) 

fseek(fp,(long)i*sizeof(struct record),0); 
fwrite((char *)r,sizeof(struct record),1,fp); 


问题在这里。你上来就 i=4,让文件指针转到那个地方。
可是此时文件里还没有东西呢,文件指针怎么会转到那个地方呢。

---------------------------

 whyglinux 回复于:2004-04-28 11:43:54
就象前面 飞灰橙 告诉你的,原来的程序没有问题。你说datafile文件是空的,有什么根据没有?检查一下文件的大小不就知道了。在我这里它的大小为 80 字节。

 liangtf 回复于:2004-04-28 12:26:42
仔细检查一下吧,极可能是人为原因。

 FH 回复于:2004-04-28 12:57:35
[quote:f06884cb47="liangtf"]仔细检查一下吧,极可能是人为原因。[/quote:f06884cb47]
逻辑上都N处错误了,还“极可能”“人为”?
I服了U!

 whyglinux 回复于:2004-04-28 13:08:01
>> 逻辑上都N处错误了,还“极可能”“人为”?

不知 FH 所说的 N 处错误表现在哪里,能给我们说说吗?我实在看不出楼主的程序有什么明显的错误。

 liangtf 回复于:2004-04-28 13:10:49
[quote:4321a4144e="FH"]
逻辑上都N处错误了,还“极可能”“人为”?
I服了U![/quote:4321a4144e]

不懂,望指点。

 windflowers1976 回复于:2004-04-28 13:13:56
[quote:391e3108a4="zkyguanl"]void putrec(FILE *fp,int i,struct record *r) 

fseek(fp,(long)i*sizeof(struct record),0); 
fwrite((char *)r,sizeof(struct record),1,fp); 


问题在这里。你上来就 i=4,让文件指针转到那个地方。..........[/quote:391e3108a4]

程序没有问题.即使文件刚刚生成,文件指针也是可以移动的,请查阅APUE.中文版39页.
[b:391e3108a4]"文件位移量可以大于文件当前长度,...形成空洞文件"[/b:391e3108a4]
上述的代码运行结果如下
===============================================
在UE中打开
    user1 烫烫烫   user2 烫烫烫   user3 烫烫烫   user4 烫烫烫   user5 烫烫烫
文件大小为80 字节 (80 字节)
所以,说明执行成功,写入四条记录,大小为4*sizeof(record)
===============================================
回答问题,最好不要主管臆断,试验下先,更不要说什么"人为错误的"唯心主义论调.

 zkyguanl 回复于:2004-04-28 13:15:51
虚心接受。

 windflowers1976 回复于:2004-04-28 13:19:05
下面是用16进制打开的文件datafile
可以分析,已经成功写入了数据.






 liangtf 回复于:2004-04-28 13:20:42
[quote:34359c895c="windflowers1976"]龅拇朐诵薪峁缦?
===============================================
在UE中打开
    user1 烫烫烫   user2 烫烫烫   user3 烫烫烫   user4 烫烫烫   user5 烫烫烫
文件大小为80 字节 (80 字节)
所以,说..........[/quote:34359c895c]

喂,老兄你怎么知道没试验???可我用的是MDK的Linux系统
程序是没问题的,当然就出在系统或是其它方面的原因了,和程序代码本身原因比这难道不能说成是人为原因吗?

难道楼主成功了硬要说是空文件?!

主要是帮助别人解决问题,而我们不在用户环境下,还能怎么做?

不要妄下结论!

 windflowers1976 回复于:2004-04-28 13:36:18
呵呵,不用生气.言语不当见谅.不是说你没有试验.
程序是没有问题的,不会产生空文件.
如果楼上的同志还以为是空文件,那请用WINDOWS UE和UNIX的HD命令打开看看.

 FH 回复于:2004-04-28 14:41:37
【逻辑错误】看这里:
for(i=4;i>=0;i--) 
先用i=4调用putrec,对于空文件,fseek会怎样?
fseek和fwrite的返回码都不判!
对于空文件来说,最后写出的文件中应该包含几个记录,谁能说清楚?应该是3条记录而不是5条记录吧?
是不是产生空洞好像和系统相关吧?

 fwizard 回复于:2004-04-28 14:43:45
谢谢,各位的关注,我的本意是倒着取数据,写到datafile里后,能看到正序的数据,主要是实验fseek的功能

vi datafile
显示内容如下(属性显示大小80k),现在有两个问题:
1.显示的内容显然不是我所要得到的,即使是大家实验的结果有点接近,怎么把"烫"去掉呢?
2.在我的solaris系统中直接打开该文件,看到的是空白的,也就是我前面所谓的程序没有写东西

[quote:6c7e483bf8]ser1^C\377\314^Auser2^C\377\314^Buser3^C\377\314^Cuser4^C\377\314^Duser5
^C\377\314[/quote:6c7e483bf8]

 windflowers1976 回复于:2004-04-28 14:49:29
[quote:e2d2a0c21b="fwizard"][/quote:e2d2a0c21b]
1.什么是你想要的结果.
2.vi 大概不能直接打开二进制文件吧.
3.用hd datafile 看文件的内容.
4.我去SUN 和LINUX SCO HP 下跑跑看.
5.少等.

 fwizard 回复于:2004-04-28 15:05:55
这个程序主要想说明fseek函数的定位功能,它按逆序取出结构数组logins的各个元素并按此顺序将它们输出到文件中,但是输出的数组元素在文件中仍然按元素顺序排列.为此,在函数putrec中用fwrite每输出一个结构成员之前,先掉用fseek定位该元素在文件中的位置,这个位置是相对文件开始的位置,其偏移量为i*sizeof(struct record)个字节,i的数组元素下标.

请指教

 windflowers1976 回复于:2004-04-28 15:11:16
[b:88b0a4db13]在LINUX SCO HP-UX 下测试;我手头的SUN 没有编译器;[/b:88b0a4db13]
[code:1:88b0a4db13]#include <stdio.h> 
#include <stdlib.h>
//#include "err_exit.h" 


struct record{ 
int uid; 
char login[9]; 
}; 

char *logins[]={"user1","user2","user3","user4","user5"}; 

void putrec(FILE *fp,int i,struct record *r) 

   fseek(fp,(long)i*sizeof(struct record),0); 
   fwrite((char *)r,sizeof(struct record),1,fp); 


int main(void) 

   int i; 
   FILE *fp; 
   struct record rec; 

   if ((fp = fopen("datafile","w"))==NULL) 
      //err_exit("datafile"); 
   {
      perror("open");
      exit(-1);
   }

   for(i=4;i>=0;i--) 
   { 
      memset ( &rec, 0, sizeof(struct record) );
      rec.uid=i; 
      strcpy(rec.login,logins[i]); 
      putrec(fp,i,&rec); 
   } 
   fclose(fp); 
   exit(0); 
}[/code:1:88b0a4db13]











 windflowers1976 回复于:2004-04-28 15:12:20
SCO 测试






 windflowers1976 回复于:2004-04-28 15:16:12
WINDOWS vc6测试






 windflowers1976 回复于:2004-04-28 15:22:55
[b:ddbdd76714][quote:ddbdd76714="FH"]【逻辑错误】看这里:
fseek和fwrite的返回码都不判!
[/quote:ddbdd76714][/b:ddbdd76714]
如果是按照楼主最后说明的要求,同意FH的说法.
不判断返回码就好像把1000$钱往口袋里放却不管口袋有没漏洞,放进去就行.

 fwizard 回复于:2004-04-28 15:34:19
谢谢楼上的大哥
对二进制文件一直没有深的认识
1?写入文件的是数组或者是结构体数据,自然就是二进制文件了?
2?二进制文件直接打开后什么都看不到吗?
3?在userx后面总是有一个多的东西,是怎么出来的,我这里solaris下more之后后面都有个况字
[quote:c662af4d2d]user1况user2况user3况user4况user5况[/quote:c662af4d2d]

 carol1980 回复于:2004-04-28 16:12:00
1?写入文件的是数组或者是结构体数据,自然就是二进制文件了? 

不一定

2?二进制文件直接打开后什么都看不到吗? 

如果对应的 ascii 值是不可见字符,就有可能看不到或者乱码

3?在userx后面总是有一个多的东西,是怎么出来的,我这里solaris下more之后后面都有个况字 

这个就是乱码,建议直接看二进制数据 "%02x"  :em17:  偶的笨办法

 jiutiao2004 回复于:2004-04-29 13:33:07
我试了一下读出文件

#include <stdio.h> 


struct record{ 
int uid; 
char login[9]; 
}; 


int main(void) 

int i; 
FILE *fp; 
struct record rec; 

if ((fp = fopen("datafile","r"))==NULL) 
{
  printf("open file error");
  return;
}

struct record a;
for(i=4;i>=0;i--) 

fread(&a,sizeof(struct record),1,fp);
printf("%d--%s\n",a.uid,a.login);


fclose(fp); 
exit(0); 


可以读出,

0--user1
1--user2
。。。。

是否是,系统自己加入的某些字符,用于分割结构体

 windflowers1976 回复于:2004-04-29 14:04:51
[quote:965e82eca0="jiutiao2004"] 


int main(void) 

int i; 
FILE *fp; 
struct record rec; 

if ((fp = fopen("datafile","r"))==NULL) 
{
  printf("open file error");
  return;
}

struct record a;
for(i=4;i>=0;i--..........[/quote:965e82eca0]
NOD.

 whyglinux 回复于:2004-04-29 19:57:02
把这个帖子中出现的一些问题归纳了一下。如有不同看法,欢迎继续讨论。

1. 楼主给出的程序是正确的,执行程序能得到预期的结果。这个程序其实出自一本介绍在 Unix 下 C 编程的书(具体叫什么记不得了),以前看到过。

2. 楼主刚开始说产生的文件是“空文件”(真正的结果不是空文件),那只是因为他对文件的检查方式不对。到底文件内容是否为空,不是“看”(指用显示文件内容的命令或者用编辑软件)它能不能显示出东西来,而是要通过查看文件的大小来获知。所谓的“看”,只能使用于文本文件,对非文本文件不适用。

3. fwrite 函数使用二进制方式写文件。例如,一个整数如果用 4 个字节表示,则在文件中一个整数始终占有 4 个字节,不论整数的大小;与此不同,如果是用字符方式写文件,则一个整数在文件中占有的字节数与整数的大小(整型数字的个数)有关(当然还跟规定的格式有关,如果有的话)。

4. 对于二进制文件,它是用来被使用的(如读取、执行等),而不是用来看的(显示它的内容没有实际意义)。比如这个程序产生的二进制文件 datafile,它的作用就是用同样的结构体变量来读取,如上面的 jiutiao2004  给出的程序所示例的那样;这也是检查楼主的程序写 datafile 是否成功的可靠方法。

5. 很多人也已经注意到了,写到 datafile 中的东西并不都是我们所需要的数据,其中还夹杂着一些“垃圾”。产生的原因是由于结构体的“位对齐”造成的(可参见以前的一些这方面的讨论,如这个帖子:http://bbs.chinaunix.net/forum/viewtopic.php?t=68019&highlight=%BD%E1%B9%B9%CC%E5)。本来结构体 record 的大小是 4+9 = 13 个字节,但是由于位对齐的原因,其实际大小是 16。另外,login 字符数组中的字符也并不是全部都是有效字符,这也是产生垃圾的一个原因。垃圾字符的存在不影响 datafile 的使用,不是错误。

6. 关于 fseek 函数。其功能是用来定位文件读写指针。如果文件指的是设备文件,则 fseek 的行为是不确定的;如果文件是一个磁盘文件,则 fseek 函数可以把文件指针指向文件结束符的后面。这时可以从这个位置起写数据,而这之前和原来文件结尾之间的“空隙”部分将用 0 来填充。因此,FH 的担心是多余的。他在上面是这样写的:
>> 【逻辑错误】看这里:
>> for(i=4;i>=0;i--)
>> 先用i=4调用putrec,对于空文件,fseek会怎样?
>> fseek和fwrite的返回码都不判!
>> 对于空文件来说,最后写出的文件中应该包含几个记录,谁能说清楚?应该是3条记录而不是5条记录吧?
>> 是不是产生空洞好像和系统相关吧? 

所以对于空文件,象楼主那样使用fseek也不会有错误。另外,对于这个程序,即使对 fseek 和 fwrite 的返回值不作判断也是可以的,没有什么强有力的理由(比如算法上的要求,这一点要与 fread 函数区别)要求一定这么做,更谈不上是逻辑(算法)上的错误。

 FH 回复于:2004-04-29 21:09:58
to whyglinux:
1.我没见过哪个文档里面说到fseek可以定位于文件逻辑结束之后的,如果有,麻烦向我推荐一下;如果没有,我不赞成这样使用,即使它被许多环境证明了。因为任何公司都不会保障未公开文档的功能的延续性。
2.关于fseek与fwrite的返回码问题,我仍坚持我的观点,至少可以从fseek的返回码判断是否成功定位于文件逻辑结束之后吧?此外,我觉得fwrite也应该判,至少我的程序里面都判,除非是写log的时候。
3.如果fseek并不能提供所特需的定位功能,那么这个程序是不是有逻辑上的错误呢?我是就此前提论事。

 windflowers1976 回复于:2004-04-30 07:41:23
[quote:329b715d0e="FH"]to whyglinux:
1.我没见过哪个文档里面说到fseek可以定位于文件逻辑结束之后的,如果有,麻烦向我推荐一下;如果没有,我不赞成这样使用,即使它被许多环境证明了。因为任何公司都不会保障未公开文档的功能的延续性..........[/quote:329b715d0e]
我查了下 APUE 中文版39页说过 lseek 这样做是可以的,fseek 没有说可以;可以的原因我估计fseek  是对lseek 的上层封装.
其他同意你的观点.
有义务保证自己被调用的代码非常健壮,没有权力要求别人或调用的代码非常健壮.
基于上述的论调,不管是捕捉异常的办法或判断返回码,为了健壮性,程序的运行过程中检查错误是被要求的.

 FH 回复于:2004-04-30 09:19:14
to windflowers1976:
我也觉得lseek可以的话fseek也应该可以。谢谢。
不过我在几个环境的man中都没有见到对此的说明。

 whyglinux 回复于:2004-04-30 10:13:51
[quote:825bcc8bd7="FH"]to whyglinux:
1.我没见过哪个文档里面说到fseek可以定位于文件逻辑结束之后的,如果有,麻烦向我推荐一下;如果没有,我不赞成这样使用,即使它被许多环境证明了。因为任何公司都不会保障未公开文档的功能的延续性..........[/quote:825bcc8bd7]

这里有一篇SunOS的man明确提到了这一点:[url]http://www.cs.biu.ac.il/cgi-bin/man?fseek+3C[/url],还有这里:[url]http://www.opengroup.org/onlinepubs/007908799/xsh/fseek.html[/url]。在一般的linux文档中好像是都没有此项说明,但是却能在这种情况下使用,说明还是支持fseek的越过文件尾部写数据的。

 FH 回复于:2004-04-30 10:36:47
[quote:dfab6506fb="whyglinux"][/quote:dfab6506fb]
谢谢。

忽然想到VM好像就是这样写的,结果在Windows文件系统里留下很多碎片,都不能清理。

 whyglinux 回复于:2004-04-30 10:52:41
碎片产生的原因应该是多次写文件造成的,与 fseek 是否越界(指文件尾部)使用无关。但是可能与 fseek 的频繁使用有关,因为 fseek 会刷新文件缓冲。其实不只是 fseek,还有很多途径可以造成频繁写文件的操作,这应该属于程序员的问题了。我是这么理解的。

 FH 回复于:2004-04-30 11:06:34
[quote:de88588aee="whyglinux"]碎片产生的原因应该是多次写文件造成的,与 fseek 是否越界(指文件尾部)使用无关。[/quote:de88588aee]
呵呵,不敢苟同。
我觉得这与其产生的缝隙有绝对的关系,Windows认为这就是碎片,不能移动。
顺便说一下,我用的是预分配空间方式。

 蓝色键盘 回复于:2004-04-30 11:07:01
这个贴子讨论的人真多。

一个是fseek,一个是fwrite
| 设为精彩回复,大家继续,呵呵

 windflowers1976 回复于:2004-04-30 12:33:00
[code:1:a7ebe9ffda]/* Copyright (C) 1991, 92, 93, 95, 96, 97 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <errno.h>
#include <stdio.h>


/* Move the file position of STREAM to OFFSET
   bytes from the beginning of the file if WHENCE
   is SEEK_SET, the end of the file is it is SEEK_END,
   or the current position if it is SEEK_CUR.  */
int
fseek (stream, offset, whence)
     register FILE *stream;
     long int offset;
     int whence;
{
  long int o;

  if (!__validfp (stream))
    {
      __set_errno (EINVAL);
      return EOF;
    }

  /* Write out any pending data.  */
  if (stream->__mode.__write && __flshfp (stream, EOF) == EOF)
    return EOF;

  /* Make sure we know the current offset info.  */
  stream->__offset = -1;
  if (__stdio_check_offset (stream) == EOF)
    return EOF;

  /* We are moving the file position, so we are no longer at EOF.  */
  stream->__eof = 0;

  if (stream->__pushed_back)
    {
      /* Discard the character pushed back by ungetc.  */
      stream->__bufp = stream->__pushback_bufp;
      stream->__pushed_back = 0;
    }

  /* Check the WHENCE argument for validity, and process OFFSET
     into an absolute position in O.  By the end of this switch,
     either we have returned, or O contains an absolute position.  */
  o = offset;
  switch (whence)
    {
    default:
      __set_errno (EINVAL);
      return EOF;

    case SEEK_END:
      /* We don't know where the end of the file is,
 so seek to the position in the file the user asked
 for, and then look where that is.  */
      if (stream->__io_funcs.__seek == NULL)
{
  __set_errno (ESPIPE);
  return EOF;
}
      else
{
  fpos_t pos = (fpos_t) o;
  if ((*stream->__io_funcs.__seek)
      (stream->__cookie, &pos, SEEK_END) < 0)
    {
      if (errno == ESPIPE)
stream->__io_funcs.__seek = NULL;
      return EOF;
    }
  stream->__offset = pos;
  /* Make O be absolute, rather than
     relative to the end of the file.  */
  o = pos;
}

      /* Fall through to try an absolute seek.  */

    case SEEK_SET:
      /* Make O be relative to the buffer.  */
      o -= stream->__target;
      /* Make O be relative to the current position in the buffer.  */
      o -= stream->__bufp - stream->__buffer;

      /* Fall through to see if we can do it by
 moving the pointer around in the buffer.  */

    case SEEK_CUR:
      /* If the offset is small enough, we can just
 move the pointer around in the buffer.  */

#if 0 /* Why did I think this would ever work???  */
      if (stream->__put_limit > stream->__buffer)
{
  /* We are writing.  */
  if (stream->__bufp + o >= stream->__buffer &&
      stream->__put_limit > stream->__bufp + o &&
      stream->__get_limit > stream->__bufp + o)
    {
      /* We have read all the data we will change soon.
 We can just move the pointer around.  */
      stream->__bufp += o;
      return 0;
    }
  else
    {
      /* Flush the buffer.  */
      if (__flshfp(stream, EOF) == EOF)
return EOF;
    }
} else
#endif
      if (o < 0 ?
  (-o <= stream->__bufp - stream->__buffer) :
  (o <= stream->__get_limit - stream->__bufp))
{
  stream->__bufp += o;
  return 0;
}

      /* Turn it into an absolute seek.  */
      o += stream->__bufp - stream->__buffer;
      o += stream->__target;
      break;
    }

  if (o < 0)
    {
      /* Negative file position is meaningless.  */
      __set_errno (EINVAL);
      return -1;
    }

  /* O is now an absolute position, the new target.  */
  stream->__target = o;

  /* Set bufp and both end pointers to the beginning of the buffer.
     The next i/o will force a call to the input/output room function.  */
  stream->__bufp
    = stream->__get_limit = stream->__put_limit = stream->__buffer;

  /* Make sure __flshfp doesn't think the put_limit is at the beginning
     of the buffer because of line-buffering magic.  */
  stream->__linebuf_active = 0;

  /* If there is no seek function, seeks always fail.  */
  if (stream->__io_funcs.__seek == NULL)
    {
      /* This is preemptive, since we don't actually do the seeking.
 But it makes more sense for fseek to to fail with ESPIPE
 than for the next reading or writing operation to fail
 that way.  */
      __set_errno (ESPIPE);
      return EOF;
    }

  /* Don't actually seek.  The next reading or writing operation
     will force a call to the input or output room function,
     which will move to the target file position before reading or writing.  */
  return 0;
}[/code:1:a7ebe9ffda]

 whyglinux 回复于:2004-04-30 14:10:13
[quote:398d666984="FH"]
呵呵,不敢苟同。
我觉得这与其产生的缝隙有绝对的关系,Windows认为这就是碎片,不能移动。
顺便说一下,我用的是预分配空间方式。[/quote:398d666984]

FH 多次提到“缝隙”这个概念,认为文件中真的存在着这么一个空白区域,真的是这样吗?

缝隙这个概念是在使用 fseek 定位文件指针、并且文件指针位置越过了文件结尾时产生的一个概念,此刻它指的是目前文件指针位置和原来文件结束位置之间的这片区域。注意,此时磁盘文件中并没有这样的一片区域,因为你还没有进行写文件的操作。

如果此时进行一个写文件的操作,根据 fseek 函数的说明,这块所谓的“缝隙”区域会用 0 来填充,然后随同要写的数据一起送到文件缓冲(即真正出现在文件中的内容)。从写文件开始起,“缝隙”这个概念已经不存在了,在磁盘文件中更不会有上述“缝隙”区域的存在。

为了说明这个问题,写了下面两个程序。程序功能分别是写一个100字节的文件(除了最后一个字节为字符 'a' 外,文件的其它内容为 0),区别就是一个不用 fseek,一个用fseek。特别请 FH 检验一下两种方式产生的两个文件有什么不同,fseek 带来的“缝隙”到底对最终文件有无影响。至少,我看不出这两个文件(tst1、tst2)有什么不同。
[code:1:398d666984]
#include <stdio.h>

#if 1 // <= change the 1 here to 0 to compile program 2

// program 1: without using fseek()
int main()
{
  FILE *fp;
  char c[99];
  int i;
  for (i=0; i<99; i++) {
    c[i] = 0;
  }
  fp = fopen("tst1", "w");
  fwrite(c, 1, 99, fp);
  fwrite("a", 1, 1, fp);
  fclose(fp);
}

#else

// program 2: with using fseek()
int main()
{
  FILE *fp;
  fp = fopen("tst2", "w");
  fseek(fp, 100-1, 0);
  fwrite("a", 1, 1, fp);
  fclose(fp);
}

#endif
[/code:1:398d666984]fseek 函数的源代码

另外,上面windflowers1976贴出的 fseek 函数的源代码说明文件指针越过文件前面是非法的,但是指到文件结尾之后却没有问题。但是 fseek 的源代码还不能说明此时的 fwrite 或者 fread 函数是怎样处理这一情况的,关于这一点,要到 fwrite 和 fread 的代码中去查找吧。

 windflowers1976 回复于:2004-04-30 14:52:43
[b:2f8f2a0200] /* Don't actually seek.  The next reading or writing operation 
     will force a call to the input or output room function, 
     which will move to the target file position before reading or writing.  */ [/b:2f8f2a0200][code:1:2f8f2a0200]
/* Copyright (C) 1991, 92, 93, 94, 96, 97, 98 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <errno.h>
#include <stdio.h>
#include <string.h>


/* Write NMEMB chunks of SIZE bytes each from PTR onto STREAM.  */
size_t
fwrite (ptr, size, nmemb, stream)
     const void *ptr;
     size_t size;
     size_t nmemb;
     register FILE *stream;
{
  register const unsigned char *p = (const unsigned char *) ptr;
  register size_t to_write = size * nmemb;
  register size_t written = 0;
  int newlinep;
  size_t buffer_space;
  int default_func;

  if (!__validfp (stream) || !stream->__mode.__write)
    {
      __set_errno (EINVAL);
      return 0;
    }

  if (ferror (stream))
    return 0;
  if (p == NULL || to_write == 0)
    return 0;

  if (!stream->__seen || stream->__put_limit == stream->__buffer)
    {
      /* This stream has never been seen before.
 Calling __flshfp will give it a buffer
 and I/O functions if it needs them.  */
      if (__flshfp (stream, *p++) == EOF)
return 0;
      if (--to_write == 0)
return 1;
      else
++written;
    }

  default_func
    = stream->__room_funcs.__output == __default_room_functions.__output;

  {
    int save = errno;

    if (__stdio_check_offset (stream) == EOF && errno != ESPIPE)
      {
stream->__error = 1;
goto done;
      }

    __set_errno (save);
  }

  if (stream->__buffer == NULL && default_func &&
      stream->__offset == stream->__target)
  write_through:
    /* This is an unbuffered stream using the standard output
       buffer-flushing function, so we just do a straight write.  */
    {
      int count = (stream->__io_funcs.__write == NULL ? to_write :
   (*stream->__io_funcs.__write) (stream->__cookie,
  (const char *) p,
  to_write));
      if (count > 0)
{
  written += count;
  if (stream->__offset != -1)
    {
      stream->__offset += count;
      stream->__target = stream->__offset;
    }
  to_write -= count;
  p += count;
}
      else
stream->__error = 1;
      goto done;
    }

  /* We ignore the end pointer here since we want to find out how much space
     is really in the buffer, even for a line-buffered stream.  */
  buffer_space = stream->__bufsize - (stream->__bufp - stream->__buffer);

  newlinep = (stream->__linebuf &&
      memchr ((const void *) p, '\n', to_write) != NULL);

  if (newlinep && stream->__bufp == stream->__buffer &&
      stream->__offset == stream->__target)
    /* The buffer's empty, and we want to write our data
       out soon anyway, so just write it straight out.  */
    goto write_through;

  if (stream->__bufsize == 0 && !default_func)
    {
      /* No buffer, and a special function.
 We can't do much better than putc.  */
      while (to_write-- > 0)
{
  if (__flshfp (stream, *p++) == EOF)
    break;
  else
    ++written;
}
    }
  else if (!default_func || buffer_space >= to_write)
    {
      /* There is enough room in the buffer for everything we want to write
 or the user has specified his own output buffer-flushing/expanding
 function.  */
    fill_buffer:
      while (to_write > 0)
{
  register size_t n = to_write;

  if (n > buffer_space)
    n = buffer_space;

  buffer_space -= n;

  written += n;
  to_write -= n;

  if (n < 20)
    while (n-- > 0)
      *stream->__bufp++ = *p++;
  else
    {
      memcpy ((void *) stream->__bufp, (void *) p, n);
      stream->__bufp += n;
      p += n;
    }

  if (to_write == 0)
    /* Done writing.  */
    break;
  else if (buffer_space == 0)
    {
      /* We have filled the buffer, so flush it.  */
      if (fflush (stream) == EOF)
break;

      /* Reset our record of the space available in the buffer,
 since we have just flushed it.  */
    check_space:
      buffer_space = (stream->__bufsize -
      (stream->__bufp - stream->__buffer));
      if (buffer_space == 0)
{
  /* With a custom output-room function, flushing might
     not create any buffer space.  Try writing a single
     character to create the space.  */
  if (__flshfp (stream, *p++) == EOF)
    goto done;
  ++written;
  --to_write;
  goto check_space;
}
    }
}

      /* We have written all the data into the buffer.  If we are
 line-buffered and just put a newline in the buffer, flush now to
 make sure it gets out.  */
      if (newlinep)
fflush (stream);
    }
  else
    {
      /* It won't all fit in the buffer.  */

      if (stream->__bufp != stream->__buffer)
{
  /* There are characters in the buffer.  Flush them.  */
  if (__flshfp (stream, EOF) == EOF)
    goto done;
}

      /* The buffer has been flushed.
 Now either fill it or write directly.  */

      buffer_space = stream->__bufsize - (stream->__bufp - stream->__buffer);

      if (stream->__offset == stream->__target &&
  (buffer_space < to_write || newlinep))
/* What we have to write is bigger than the buffer,
   or it contains a newline and we're line-buffered,
   so write it out.  */
goto write_through;
      else
/* It will fit in the buffer.  */
goto fill_buffer;
    }

 done:;
  return (size_t) written / size;
}

weak_alias (fwrite, fwrite_unlocked)[/code:1:2f8f2a0200]
呵呵,内核代码有人看啊.

 fwizard 回复于:2004-04-30 15:14:56
fseek允许设置文件位置超过文件的当前文件尾,如果之后在此新文件位置写入了数据,则后续从原文件尾新新写入的数据之间的空隙中读出的字节将用0填充直至次空隙写入实际的数据为止,下面给出的程序说明用fseek定位文件文件位置超过文件尾的两种情形。第一种是以非添加方式写数据超过文件尾,此时在当前文件尾和新写入的数据之间将行成所谓的“空洞”。第二种是以添加方式写数据,此时尽管可以用fseek定位文件当前位置超过文件尾,但是写该文件时,文件的当前位置被忽略,所有的数据都添加在文件尾并且文件当前位置被重新定位到新的文件尾
[quote:350f5c39f1]
#include <stdio.h>
#include <stdlib.h>
#include "err_exit.h"

char buf[132];

int main(int argc,char *argv[])
{
  FILE *fd;
  fpos_t pos;
  if(argc != 2){
     fprintf(stderr,"Usage: %s mode \n",argv[0]);
     exit(EXIT_FAILURE);
   }
   
   if(argv[1][0]!='a'){
   if  ((fd = fopen("test_file","w+"))==NULL)
   err_exit("fopen failed");
   }
   else{
     if((fd = fopen("test_file","a+"))==NULL)
     err_exit("fopen failed");
     }
     
     
   fputs("0123456789",fd);
   fputs("ABCDEFGHIJ",fd);
   
   fseek(fd,0,SEEK_END);
   fgetpos(fd,&pos);
   printf("current file en position is %ld\n",pos);
   
   fseek(fd,30,SEEK_END);
   
   fgetpos(fd,&pos);
   printf("Now we call fseek(fd,30,SEEK_END)\n");
   printf("Current file position is %ld \n",pos);
   
   fputs("abcdefg",fd);
   printf("Now we write %c %s %c\n",'\"',"abcdefg",'\"');
   
   fgetpos(fd,&pos);
   printf("current position of file end is %ld\n",pos);
   fclose(fd);
}
[/quote:350f5c39f1]

运行示例:
[quote:350f5c39f1]# t13 a+
current file en position is 20
Now we call fseek(fd,30,SEEK_END)
Current file position is 50 
Now we write " abcefg "
current position of file end is 57
[color=red:350f5c39f1][b:350f5c39f1]current position of file end is 27[/b:350f5c39f1][/color:350f5c39f1]
# od -c test_file
0000000   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
0000020   G   H   I   J   a   b   c   d   e   f   g
[color=red:350f5c39f1]0000033[/color:350f5c39f1]


# t13 w+
current file en position is 20
Now we call fseek(fd,30,SEEK_END)
Current file position is 50 
Now we write " abcefg "
current position of file end is 57
# od -c test_file
0000000   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
0000020   G   H   I   J  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000060  \0  \0   a   b   c   d   e   f   g
0000071[/quote:350f5c39f1]

几点说明:
   1。我这上面的程序基本上都是来源于unix程序设计教程这本书,自认为对于有一定c和c++编程基础又刚接触unix编程时的一本好书。当然如果在瘟下比较牛的话,可能也不需要,我是刚参加工作,理论有实践还比较差
   2。对于前面程序涉及的东西我想几位的解释已经让我明白的差不多了,先谢谢大家的热心帮助。
   3。关于大家后面的讨论我这里照样摘录了一个程序,大家运行测试一下,就知道里边包含的意思了
   4。对这个程序我运行的结果和书上给出的结果稍有出入,欢迎大家测试和讨论。
其中第一个红色行是对应行的书上给的结果
第二个红行是我运行出来的,书上没有。

 welkin 回复于:2004-04-30 15:34:55
1

 FH 回复于:2004-04-30 18:00:06
to whyglinux:
缝隙(gap)引自你提供的man文档。缝隙这一概念显然是逻辑上的而不是物理上的,这一点不用讨论。
我说的是缝隙给Windows FAT32文件系统造成的假象,即文件碎片问题。
我的VMDK文件存在很多碎片,怎么copy都不能消除,也不能被碎片整理工具消除。

 whyglinux 回复于:2004-04-30 18:12:23
那你看看文件的属性是否是系统属性,这样的文件不允许移动。比如Windows下那个大大的临时文件好像就是这样的。试试把文件的系统属性去掉,再用碎片整理。

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有