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

时间:2017-02-04 22:59:36

标签: c file-handling fgets fgetpos

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

这是我的代码:

#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行之后的文件内容都没有改变,并且由于某种原因,当循环运行第三行时调用fgets时间,读入数组line1和line2的内容分别为“c \ n”和“ddd \ n”。

再说一遍,我不想用另一个文件来完成这个,我只需要弄明白屏幕背后到底出了什么问题

任何线索都会受到赞赏。谢谢。

3 个答案:

答案 0 :(得分:3)

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

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

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

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

  • 在更改回写到阅读之前,您应该调用fflush()将内容写回文件。

C标准为在更新模式下打开的文件指定了此限制:

  

7.21.5.3 fopen功能

     

[...]输出不能直接跟随输入,而无需对fflush函数或文件定位函数(fseekfsetpos或{{进行干预调用。 1}}),除非输入操作遇到文件结束,否则输入不应直接跟随输出,除非输入操作遇到文件结束。

这解释了为什么在以相反顺序写入行之后读取文件位置会导致问题。致电rewind应解决此问题。

以下是更正后的版本:

fflush()

答案 1 :(得分:2)

更改文件的当前位置时,可能不一定刷新缓冲区。所以必须明确刷新它。

E.g使用fflush(fp);

更改

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

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

答案 2 :(得分:2)

为什么不使用两个文件指针,两个指向同一个文件,一个读取,一个写入?无需跟踪文件位置,无需寻找,无需冲洗。

这种方法可以为你提供很多复杂的东西。这些不必要的努力更好地投入到如下的一些复杂的错误检查/记录中; - ):

#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;
}