APUE上有这么一段话:
当以读和写类型打开一文件时(type中+符号),具有下列限制:
~ 如果中间没有fflush, fseek, fsetpos或rewind,则在输出的后面不能直接跟随输入。
~ 如果中间没有fseek, fsetpos或rewind, 或则一个输入操作没有达到文件尾端,则在输入操作之后不能直接跟随输出。
让人费解的就是,为什么不能这么干!
首先,我们有一个名为 "huen" 的文本文件,内容如下:
然后,看代码
-
#include <unistd.h>
-
#include <fcntl.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
-
int main()
-
{
-
int fd = open("huen", O_RDWR);
-
FILE *fp = fdopen(fd,"rb+");
-
char readStr[8]={0};
-
char buf[BUFSIZ];
-
memset(buf,0,sizeof(buf));
-
int res = -1;
-
if( (res = setvbuf(fp,buf,_IOFBF,sizeof(buf))) != 0)
-
{
-
perror("setvbuf error");
-
}
-
else
-
{
-
fread(readStr,2,1,fp);
-
printf("readStr = %s, buf = %s, fd_pos = %d\n", readStr, buf, lseek(fd,0,SEEK_CUR));
-
char *str = "hel";
-
/*fseek(fp,0,SEEK_CUR);*/
-
fwrite(str,3,1,fp);
-
printf("buf = %s\n",buf);
-
sleep(30);
-
}
-
}
需要说明的是,虽然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:
-
[huenyifei@localhost StandardIO]$ ./io
-
readStr = 12, buf = 1234567890abc
-
, fd_pos = 14
-
buf = 12hel67890abc
"huen" 文件变为:
Solaris:
-
readStr = 12, buf = ,fd_pos = 14
-
buf =
-
"huen" 文件变为:
由此可见,为了代码的兼容性,还是要按照APUE上的限制进行编程。
如果write 后 read 会照成什么样的问题?(以下运行在 Solaris10)
"huen" 的文本文件,内容如下:
代码:
-
#include <unistd.h>
-
#include <fcntl.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
-
int main()
-
{
-
int fd = open("huen", O_RDWR);
-
FILE *fp = fdopen(fd,"rb+");
-
char readStr[8]={0};
-
char buf[BUFSIZ];
-
memset(buf,0,sizeof(buf));
-
int res = -1;
-
if( (res = setvbuf(fp,buf,_IOFBF,sizeof(buf))) != 0)
-
{
-
perror("setvbuf error");
-
}
-
else
-
{
-
char *str = "hello";
-
fwrite(str,strlen(str),1,fp);
-
printf("buf = %s, fd_pos = %d\n", buf, lseek(fd,0,SEEK_CUR));
-
/*fseek(fp,0,SEEK_CUR);*/
-
fread(readStr,3,1,fp);
-
printf("buf = %s\n",buf);
-
sleep(30);
-
}
-
}
"huen" 文件变为:
而我们的期望的输出是 "hello67890abc", 没有谁愿意fread后使原本已经fwrite的文件发生改变!
补充: fseek会导致冲洗
阅读(1302) | 评论(0) | 转发(0) |