在C中同时读取和写入文件

Reading and writing to a file at the same time in C

应该在文件中每两行交换一次,直到只剩下一行或所有行都用完。我不想在这样做时使用其他文件。

这是我的代码:

#include <stdio.h>

int main() {
    FILE *fp = fopen("this.txt", "r+");
    int i = 0;
    char line1[100], line2[100];
    fpos_t pos;
    fgetpos(fp, &pos);

    //to get the total line count
    while (!feof(fp)) {
        fgets(line1, 100, fp);
        i++;
    }

    i /= 2;  //no. of times to run the loop
    rewind(fp);

    while (i-- > 0) {  //trying to use !feof(fp) condition to break the loop results in an infinite loop
        fgets(line1, 100, fp);
        fgets(line2, 100, fp);

        fsetpos(fp, &pos);

        fputs(line2, fp);
        fputs(line1, fp);

        fgetpos(fp, &pos);
    }

    fclose(fp);
    return 0;
}

this.txt中的内容:

aaa
b
cc
ddd
ee  
ffff
gg
hhhh
i
jj

运行程序后的内容

b
aaa
ddd
cc
ddd
c
c

c


i
jj

我什至尝试使用 fseek 代替 fgetpos 只是为了得到同样的错误结果。

根据我的计算,在第二个 while 循环 运行 两次 (即前四行已被处理) 之后,光标正确地位于第 17应该是 的字节(由对 ftell(fp) 的调用返回) 甚至第 4 行之后的文件内容都没有改变,并且由于某种原因 [=16] =]在第3次循环运行ning时被调用,读入数组line1和line2的内容分别为"c\n"和"ddd\n"。

再说一次,我不想使用其他文件来完成此操作,我只需要弄清楚屏幕背后到底出了什么问题

如有任何线索,我们将不胜感激。谢谢你。

更改文件的当前位置时不一定会刷新缓冲区。所以它必须显式刷新。

例如使用fflush(fp);

改变

fputs(line2,fp);
fputs(line1,fp);

fputs(line2,fp);
fputs(line1,fp);
fflush(fp);

您的代码中存在多个问题:

  • 您不检查 fopen() 是否成功,冒着未定义行为的风险。

  • 确定总行数的循环不正确。
    在此处了解原因:Why is “while ( !feof (file) )” always wrong?

  • 您实际上不需要计算总行数。

  • 您应该调用fflush()将内容写回文件,然后再从写回读更改。

C 标准对以更新模式打开的文件指定了此限制:

7.21.5.3 The fopen function

[...] output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.

这解释了为什么仅在以相反顺序写入行后读取文件位置会导致问题。调用 fflush() 应该可以解决这个问题。

这是更正后的版本:

#include <stdio.h>

int main(void) {
    FILE *fp;
    char line1[100], line2[100];
    fpos_t pos;

    fp = fopen("this.txt", "r+");
    if (fp == NULL) {
        fprintf(stderr, "cannot open this.txt\n");
        return 1;
    }

    while (fgetpos(fp, &pos) == 0 &&
           fgets(line1, sizeof line1, fp) != NULL &&
           fgets(line2, sizeof line2, fp) != NULL) {

        fsetpos(fp, &pos);
        fputs(line2, fp);
        fputs(line1, fp);
        fflush(fp);    
    }

    fclose(fp);
    return 0;
}

为什么不使用两个文件指针,都指向同一个文件,一个用于读取,一个用于写入?无需跟踪文件位置,无需四处寻找,也无需刷新。

这种方法可以省去很多复杂的事情。那些不必要的努力最好投入到一些复杂的错误中 checking/logging 如下 ;-):

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(void) 
{
  int result = EXIT_SUCCESS;

  size_t blocks = 0;

  int l1_done = 0;
  int l2_done = 0;

  FILE *fpin = fopen("this.txt", "r");
  FILE *fpout = fopen("this.txt", "r+");

  if (NULL == fpin)
  {
    result = EXIT_FAILURE;
    perror("fopen() to for reading failed");
  }    

  if (NULL == fpout)
  {
    result = EXIT_FAILURE;
    perror("fopen() for writing failed");
  }    

  while (EXIT_SUCCESS == result && !l1_done && !l2_done)
  {
    result = EXIT_FAILURE;

    char line1[100];
    char line2[100];

    if ((l1_done = (NULL == fgets(line1, sizeof line1, fpin))))
    {
      if (ferror(fpin))
      {
        fprintf(stderr, "Reading line %zu failed.\n", 2*blocks);
        break;
      }
    }

    if ((l2_done = (NULL == fgets(line2, sizeof line2, fpin))))
    {
      if (ferror(fpin))
      {
        fprintf(stderr, "Reading line %zu failed.\n", 2*blocks + 1);
        break;
      }
    }

    {
      size_t len = strlen(line1);

      if (((sizeof line1 - 1) == len) && ('\n' != line1[len]))
      {
        fprintf(stderr, "Line %zu too long or new-line missing.\n", 2*blocks);
        break;
      } 
    }

    {
      size_t len = strlen(line2);

      if (((sizeof line2 - 1) == len) && ('\n' != line2[len]))
      {
        fprintf(stderr, "Line %zu too long or new-line missing.\n", 2*blocks + 1);
        break;
      }
    } 

    if (!l2_done)
    {
      if (EOF == fputs(line2, fpout))
      {
        fprintf(stderr, "Writing line %zu as line %zu failed.\n", 2*blocks + 1, 2*blocks);
        break;
      }
    } 

    if (!l1_done)
    {
      if (EOF == fputs(line1, fpout))
      {
        fprintf(stderr, "Writing line %zu as line %zu failed.\n", 2*blocks, 2*blocks + 1);
        break;
      } 
    }

    ++blocks;

    result = EXIT_SUCCESS;
  }

  if (EXIT_SUCCESS == result && !ll_done && l2_done)   
  {
    fprintf(stderr, "Odd number of lines.\n");
  }

  fclose(fpin);  /* Perhaps add error checking here as well ... */
  fclose(fpout);  /* Perhaps add error checking here as well ... */

  return result;
}