Linux 和 Windows 之间 fwrite 的不同行为
Posted
技术标签:
【中文标题】Linux 和 Windows 之间 fwrite 的不同行为【英文标题】:Different behaviour of fwrite between Linux and Windows 【发布时间】:2015-09-15 22:37:56 【问题描述】:我有一个用 C 编写的小示例程序。我有一个调用函数 writeFile
的主程序,该函数将一些数字写入二进制文件。然后我调用overwrite
将 0 替换为 1,最后打印结果。
这是代码:
#include <stdio.h>
/* Print the content of the file */
void printFile()
printf("Read test.dat:\n");
int r;
FILE* fp = fopen("test.dat", "rb+");
if(fp)
while(fread(&r,sizeof(int),1,fp))
printf("%d\n", r);
fclose(fp);
/* Replace 0 with 1 */
void overwrite()
int r;
FILE *fp = fopen("test.dat", "rb+");
if (fp)
int i=0;
while (i < 4 && fread(&r, sizeof(int), 1, fp))
i++;
if (r == 0)
r = 1;
fseek(fp,-sizeof(int),SEEK_CUR);
fwrite(&r,sizeof(int),1,fp);
fclose(fp);
/* Create original file */
void writeFile()
int b, b1, b2, b3, b4;
b = 3;
b1 = 2;
b2 = 0;
b3 = 4;
FILE *fp = fopen("test.dat", "wb");
if (fp)
fwrite(&b, sizeof(int), 1, fp);
fwrite(&b1, sizeof(int), 1, fp);
fwrite(&b2, sizeof(int), 1, fp);
fwrite(&b3, sizeof(int), 1, fp);
fclose(fp);
int main()
writeFile();
printf("---------BEFORE--------\n");
printFile();
printf("-----------------------\n");
printf("Overwriting...\n");
overwrite();
printf("---------AFTER---------\n");
printFile();
return 0;
这段代码适用于 Linux,但是当我在 Windows 上运行相同的代码时,输出是这样的:
---------BEFORE--------
Read test.dat:
3
2
0
4
-----------------------
Overwriting...
---------AFTER---------
Read test.dat:
3
2
1
2
不仅0被1替换,而且最后一个数字也被改变了。有人可以帮助我理解为什么会发生这种情况?
另一个问题是在overwrite
中我必须使用i
来停止while,因为没有i<4
我会得到一个无限循环(仅限Windows)。
我在使用 gcc 4.8.1(来自 MinGW)编译的 Windows 8.1 上测试了此代码。 在我的 Linux 机器上,我使用 gcc 5.1.1 测试了代码。
谢谢大家,
【问题讨论】:
如果您将0
之前的2
更改为5
,它会用5
替换最后一个数字吗?
我无法复制它。我有预期的输出序列 3、2、1、4。
我很想知道我在 VBox 机器上安装 windows 是否可重现,现在我正在安装 DevCPP 进行测试。
我刚刚做到了,它确实发生在 Windows 上。它确实用第二个数字替换了最后一个数字。
【参考方案1】:
这是因为你需要在fwrite()
之后再调用fflush()
,因为你不应该在调用fwrite()
之后再调用fread()
,而中间没有调用fflush()
,
这是与这种情况相关的标准部分
7.21.5.3
fopen
函数当以更新模式(
'+'
作为上述模式参数值列表中的第二个或第三个字符)打开文件时,可以在关联的流上执行输入和输出。但是,输出不得直接跟在输入后面没有 干预对fflush
函数或文件定位函数(fseek
、fsetpos
或rewind
)的调用,并且输入不应直接跟在没有 干预对文件定位函数的调用,除非输入操作遇到文件结尾。在某些实现中,使用更新模式打开(或创建)文本文件可能会打开(或创建)二进制流。
这部分在fopen()
函数中很奇怪,因为它涉及fread()
和fwrite()
,这是我正在寻找答案的地方。
您还可以看到我的previous answer 工作但不是因为我在其中说明的原因,而是在上面的段落中找到了解释。
【讨论】:
fclose(fp)
应该在fp != NULL
分支中(这也适用于问题中的原始代码)。
我之前的回答是错误的对不起,我必须承认它没有多大意义。
谢谢,现在可以使用了!!在您修复之前,我尝试放一些 ftell(fp) 来查看指针位置,一切似乎都很好。每次读取后指针增加,当我替换它返回的值时(在 fseek 之后),然后向前返回(在 fwrite 之后)。为什么指针似乎在正确的位置?
这是正确的行为,问题与此无关,正如您从我的最终答案中看到的那样。找不到答案真的让我很困扰。【参考方案2】:
Stream I/O 的 MSDN 状态:
只有通过对
fflush
或文件定位函数(fseek
、fsetpos
或rewind
)的介入调用,输入才能直接跟随输出。如果输入操作遇到文件末尾,则输出可以跟随输入而无需对文件定位函数进行干预调用。
所以你必须在调用 fwrite 后调用fflush()
:
void overwrite()
FILE *fp = fopen("test.dat", "rb+");
if (fp)
int r;
while (fread(&r, sizeof(int), 1, fp) == 1)
if (r == 0)
r = 1;
fseek(fp,-sizeof(int),SEEK_CUR);
fwrite(&r,sizeof(int),1,fp);
fflush(fp); /* Flush here */
fclose(fp); /* Also: call fclose() only when fopen() succeeded */
【讨论】:
好吧,我确实引用了文本并指出:“输入可以直接跟随输出,只有在中间调用 fflush 或 [...]”。我不明白这有什么问题。 哦 - 我没有注意到你也在编辑我的帖子。但我仍然看不出我的答案有什么问题:| 我明白了,它与标准文档的顺序相反,它说 only with ... 而标准说 没有介入调用 ... ... 很抱歉,请试着理解,虽然我知道我的英语很好,但这不是我的母语。 好的 - 不用担心,感谢更好的格式:) MinGW 链接到 MSVCRT:mingw.org/wiki/c99,因此 MSDN 也应该适用于这种情况。不过,引用 C 标准绝对是更好的来源。以上是关于Linux 和 Windows 之间 fwrite 的不同行为的主要内容,如果未能解决你的问题,请参考以下文章
超出EOF的PHP fwrite模式“r +”不起作用,XAMPP,Windows 10 x64 [重复]
[Linux]read/write和fread/fwrite有什么区别
Linux read/write fread/fwrite两者区别