Chinaunix首页 | 论坛 | 博客
  • 博客访问: 39291
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 285
  • 用 户 组: 普通用户
  • 注册时间: 2014-12-08 13:03
个人简介

海纳百川有容乃大,壁立千仞无欲则刚。

文章分类
文章存档

2015年(17)

2014年(12)

我的朋友

分类: LINUX

2015-01-05 18:53:07

APUE上有这么一段话:
    当以读和写类型打开一文件时(type中+符号),具有下列限制:
    ~  如果中间没有fflush, fseek,  fsetpos或rewind,则在输出的后面不能直接跟随输入。
    ~  如果中间没有fseek, fsetpos或rewind, 或则一个输入操作没有达到文件尾端,则在输入操作之后不能直接跟随输出。

让人费解的就是,为什么不能这么干!

首先,我们有一个名为 "huen" 的文本文件,内容如下:
  1. 1234567890abc

然后,看代码
  1. #include <unistd.h>
  2. #include <fcntl.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>

  6. int main()
  7. {
  8.     int fd = open("huen", O_RDWR);
  9.     FILE *fp = fdopen(fd,"rb+");
  10.     char readStr[8]={0};
  11.     char buf[BUFSIZ];
  12.     memset(buf,0,sizeof(buf));
  13.     int res = -1;
  14.     if( (res = setvbuf(fp,buf,_IOFBF,sizeof(buf))) != 0)
  15.     {
  16.         perror("setvbuf error");
  17.     }
  18.     else
  19.     {
  20.         fread(readStr,2,1,fp);
  21.         printf("readStr = %s, buf = %s, fd_pos = %d\n", readStr, buf, lseek(fd,0,SEEK_CUR));
  22.         char *str = "hel";
  23.         /*fseek(fp,0,SEEK_CUR);*/
  24.         fwrite(str,3,1,fp);
  25.         printf("buf = %s\n",buf);
  26.         sleep(30);
  27.     }
  28. }



需要说明的是,虽然fread只读2个字节,但是缓冲被设为全缓冲,而且大小为 BUFSIZ 个,所以缓冲一次会读入全部 "huen"文件的内容(包含一个),我们从 代码22行可以看出。
而此时,buf的内容为 “1234567890abc\n", fd的偏移为14字节,fp的偏移为2字节
如果接下来直接 fwrite, 可能则会导致将 hel 三个字节接在 fp 原来的偏移量之后,形成新的 buf = "12hel" 并接在原来fd的偏移量之后输出。
而这明显不是我们想要的输出,我们的本意是希望 hel 这3个字符能替换掉 345 这三个字符

这里之所以使用"可能"这个词是因为,在 Linux 和BSD系统中能正确输出,但是在Solaris 系统上将出现错误,其中在 Linux的 “man 3 fopen” 中有这么一段话:

       Reads and writes may be intermixed on read/write streams in any order.  Note that ANSI C requires that a file  positioning  function  intervene  between
       output  and  input,  unless  an  input  operation encounters end-of-file.  (If this condition is not met, then a read is allowed to return the result of
       writes other than the most recent.)  Therefore it is good practice (and indeed sometimes necessary under Linux) to put an fseek() or fgetpos() operation
       between  write and read operations on such a stream.  This operation may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its synchroniz-
       ing side effect.

以下是 Centos5 和 Solaris10 的输出:

Linux:

  1. [huenyifei@localhost StandardIO]$ ./io
  2. readStr = 12, buf = 1234567890abc
  3. , fd_pos = 14
  4. buf = 12hel67890abc
"huen" 文件变为:

  1. 12hel67890abc

Solaris:

  1. readStr = 12, buf = ,fd_pos = 14
  2. buf =

"huen" 文件变为:
  1. 1234567890abc
  2. 12hel

由此可见,为了代码的兼容性,还是要按照APUE上的限制进行编程。

如果write 后 read 会照成什么样的问题?(以下运行在 Solaris10)

"huen" 的文本文件,内容如下:
  1. 1234567890abc
代码:
  1. #include <unistd.h>
  2. #include <fcntl.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>

  6. int main()
  7. {
  8.     int fd = open("huen", O_RDWR);
  9.     FILE *fp = fdopen(fd,"rb+");
  10.     char readStr[8]={0};
  11.     char buf[BUFSIZ];
  12.     memset(buf,0,sizeof(buf));
  13.     int res = -1;
  14.     if( (res = setvbuf(fp,buf,_IOFBF,sizeof(buf))) != 0)
  15.     {
  16.         perror("setvbuf error");
  17.     }
  18.     else
  19.     {
  20.         char *str = "hello";
  21.         fwrite(str,strlen(str),1,fp);
  22.         printf("buf = %s, fd_pos = %d\n", buf, lseek(fd,0,SEEK_CUR));
  23.         /*fseek(fp,0,SEEK_CUR);*/
  24.         fread(readStr,3,1,fp);
  25.         printf("buf = %s\n",buf);
  26.         sleep(30);
  27.     }
  28. }

"huen" 文件变为:
  1. hello90abc
而我们的期望的输出是 "hello67890abc", 没有谁愿意fread后使原本已经fwrite的文件发生改变!
补充: fseek会导致冲洗


阅读(1255) | 评论(0) | 转发(0) |
0

上一篇:Linux重定向

下一篇:我的apue.h配置

给主人留下些什么吧!~~