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&lt;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 函数或文件定位函数(fseekfsetposrewind)的调用,并且输入不应直接跟在没有 干预对文件定位函数的调用,除非输入操作遇到文件结尾。在某些实现中,使用更新模式打开(或创建)文本文件可能会打开(或创建)二进制流。

这部分在fopen() 函数中很奇怪,因为它涉及fread()fwrite(),这是我正在寻找答案的地方。

您还可以看到我的previous answer 工作但不是因为我在其中说明的原因,而是在上面的段落中找到了解释。

【讨论】:

fclose(fp) 应该在fp != NULL 分支中(这也适用于问题中的原始代码)。 我之前的回答是错误的对不起,我必须承认它没有多大意义。 谢谢,现在可以使用了!!在您修复之前,我尝试放一些 ftell(fp) 来查看指针位置,一切似乎都很好。每次读取后指针增加,当我替换它返回的值时(在 fseek 之后),然后向前返回(在 fwrite 之后)。为什么指针似乎在正确的位置? 这是正确的行为,问题与此无关,正如您从我的最终答案中看到的那样。找不到答案真的让我很困扰。【参考方案2】:

Stream I/O 的 MSDN 状态:

只有通过对fflush 或文件定位函数(fseekfsetposrewind)的介入调用,输入才能直接跟随输出。如果输入操作遇到文件末尾,则输出可以跟随输入而无需对文件定位函数进行干预调用。

所以你必须在调用 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两者区别

linux下php fwrite无法写入文件怎么回事呀

linux下使用fread和fwrite的问题:我想实现一个文件到另一个文件的简单复制,总是编译出错.

Linux 打开文件并写入一段字符串,同一时候读出相应文件的信息--fopen()/fwrite()/fread()