【问题标题】:The use of "r+" in fopen on windows vs linuxwindows vs linux上fopen中“r+”的使用
【发布时间】:2013-02-14 15:44:03
【问题描述】:

我正在玩弄一些打开、读取和修改文本文件的代码。一个快速(简化)的例子是:

#include <stdio.h>
int main()
{
    FILE * fp = fopen("test.txt", "r+");
    char line[100] = {'\0'};
    int count = 0;
    int ret_code = 0;
    while(!feof(fp)){
        fgets(line, 100, fp);
        // do some processing on line...
        count++;
        if(count == 4) {
          ret_code = fprintf(fp, "replaced this line\n");
          printf("ret code was %d\n", ret_code);
          perror("Error was: ");
        }
    }
    fclose(fp);
    return 0;
}

现在在 Linux 上,使用 gcc (4.6.2) 编译此代码运行,并修改文件的第 5 行。在使用 Visual C++2010 编译的 Windows7 上运行的相同代码运行并声称已成功(报告返回代码为 19 个字符,perror 表示“无错误”)但未能替换该行。

在 Linux 上,我的文件具有完全权限:

-rw-rw-rw- 1 mike users 191 Feb 14 10:11 test.txt

据我所知,在 Windows 上也是如此:

test.txt(右键)->属性->安全
为用户、系统和管理员的读写检查“允许”。

我在 Windows 上使用 MinGW 的 gcc 得到了相同的结果,所以我知道这不是 Visual C++ 的“功能”。

我是否遗漏了一些明显的东西,或者我没有得到任何错误,但也没有输出只是在 Windows 上使用 r+fopen() 的一个未记录的“功能”?


编辑:
似乎即使在Microsoft's site,他们也说“r+”应该打开以供阅读写作。他们还做了这样的说明:

当“r+”、“w+”或“a+”访问类型被指定时,读取和写入都被允许(文件被称为“更新”打开)。但是,当您在读取和写入之间切换时,必须有中间的 fflush、fsetpos、fseek 或 rewind 操作。如果需要,可以为 fsetpos 或 fseek 操作指定当前位置。

所以我尝试了:

        ...
        if(count == 4) {
          fflush(fp);
          ret_code = fprintf(fp, "replaced this line\n");
          fflush(fp);
          printf("ret code was %d\n", ret_code);
          ...

无济于事。

【问题讨论】:

  • 你有没有试过在写操作后使用fflush刷新流?
  • 我不确定这是否是问题所在,但在从文件读取和写入之间切换时,您可能需要调用fseek
  • @KristerAndersson - 是的,刚刚更新以澄清我在 MSDN 网站上看到说明你应该通过 f&lt;operations&gt;() 之一更新流所以我在之前和之后尝试了 fflush()ing写,但没有骰子。
  • @Mike:在程序退出之前尝试关闭文件流。
  • @Mike - 这可能与换行有关吗?我认为windows使用\r\n将linux使用\n

标签: c linux windows file-io fopen


【解决方案1】:

根据Linux man page for fopen()

读和写可以以任何顺序混合在读/写流上。 请注意,ANSI C 要求文件定位函数介入 在输出和输入之间,除非遇到输入操作 文件结束。 (如果不满足此条件,则允许读取 返回除最近写入之外的写入结果。)因此它 是一种很好的做法(在 Linux 下确实有时是必要的) 写入和读取操作之间的 fseek(3) 或 fgetpos(3) 操作 在这样的溪流上。此操作可能是明显的无操作(如 fseek(..., 0L, SEEK_CUR) 调用它的同步副作用。

因此,在从文件读取和写入之间切换时,您应该始终调用fseek()(例如fseek(..., 0, SEEK_CUR))。

【讨论】:

  • 这就是问题所在,这就是我阅读 MSDN 文档而不是手册页所得到的。 :) 谢谢。
  • 请注意,来自 MSDN 的最新文档(适用于 VS 2012)已更新:msdn.microsoft.com/en-us/library/yeby3zcb.aspx
  • @MichaelBurr - 好点,这不是最新的文档,我应该更新我的书签,我已经有一段时间没有去过那里了。
【解决方案2】:

在输入后执行输出之前,fflush() 没有任何好处 - 您需要执行查找操作。比如:

fseek(fp, ftell(fp), SEEK_SET); // not fflush(fp);

来自 C99 标准(7.19.5.3/6“fopen 函数):

当使用更新模式打开文件时('+' 作为第二个或第三个 上述模式参数值列表中的字符),输入和 可以在关联的流上执行输出。然而,输出 在没有介入调用的情况下,不应直接跟随输入 fflush 函数或文件定位函数 (fseek, fsetpos, 或 rewind), 输入不能直接跟在输出后面 没有对文件定位函数的干预调用,除非 输入操作遇到文件尾。

【讨论】:

  • 这很有趣,你说“fflush() 不好”,但是你引用的 C99 sn-p 说:without an intervening call to the fflush function OR to a file positioning function。所以,那里写着fflush()fseek()/其他定位函数。
  • 那是输出,然后是输入。您的问题是在执行输入后输出,这需要介入文件定位调用(或 EOF)。我要说的是,要想把所有的条件都直接记在脑子里,读起来有点棘手——一张桌子会更好。
  • +1 明白了,这说明了为什么排序很重要。感谢您的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-12
  • 1970-01-01
相关资源
最近更新 更多