为什么 fputc() 附加到我的文件而不是覆盖它?

Why does fputc() append to my file and not overwrite it?

我正在编写一个 C 程序,将某个文件中的所有大写字符更改为小写字符。问题是,在将所有字符(已处理)存储在一个字符串中,并尝试打印该文件中的所有字符后,它们被附加到该文件,而不是被覆盖。我正在使用 "r+" 作为权限。

这是我的代码:

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

#define MAX 1024

int main(int argc, char **argv)
{
    if (argc != 2) {
        printf("Invalid number of arguments.\n");
        exit(1);
    }

    FILE *f = fopen(argv[1], "r+");
    if (f == NULL)
    {
        printf("Could not open specified file.\n");
        exit(2);
    }

    char buf[MAX];
    int len = 0;
    
    while(!feof(f))
    {
        int c = fgetc(f);
        if (c >= 'A' && c <= 'Z')
            c = tolower(c);
        if (c != EOF) {
            buf[len] = c;
            len++;
        }
    }

    for (int i = 0; buf[i] != '[=10=]'; i++)
        fputc(buf[i], f);

    fclose(f);

    return 0;
}

fputs(buf, f) 也有同样的问题。此外,即使在附加之后,一些“奇怪”的字符也会出现在所选文件的末尾。

到底是什么问题?我该如何解决?谢谢你。帮助将不胜感激。

由于多种原因,您的程序有不正确甚至未定义的行为:

  • while(!feof(f)) 无法可靠地检测到文件结尾,feof() 已设置 尝试从 [=15= 读取之后] 失败。你应该改为写:

      while ((c = getc(f)) != EOF) {
          if (isupper(c))
              c = tolower(c);
          ...
    
  • 使用 if (c >= 'A' && c <= 'Z') 测试大写字母适用于 ASCII,但对于 EBCDIC 会失败。您可以改用 isupper(c) 或无条件地使用 tolower(c).

  • 读取和写入同一个文件,使用 "r+" 打开读取和更新模式,需要调用 fseek()rewind() 以从读取更改写作,反之亦然。

  • 您不检查 len 是否在 buf 的边界内,导致在尝试写入超出数组末尾的足够长输入时出现未定义的行为。

  • 您没有在 buf[len] 处设置空终止符,因此调用 fputs(buf, f) 具有未定义的行为,因为 buf 不是正确的 C 字符串。同样,发布的代码中的循环迭代 while buf[i] != '[=27=]' 这会导致未定义的行为,因为在从文件读取的数据末尾未设置此空终止符。

这是修改后的版本:

#include <ctype.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    FILE *f;
    int c;

    if (argc != 2) {
        printf("Invalid number of arguments.\n");
        return 1;
    }

    if ((f = fopen(argv[1], "r+")) == NULL) {
        printf("Could not open file %s.\n", argv[1]);
        return 2;
    }

    while ((c = getc(f)) != EOF) {
        if (isupper(c) {
            fseek(f, -1L, SEEK_CUR);
            putc(tolower(c), f);
            fseek(f, 0L, SEEK_CUR);
        }
    }
    fclose(f);
    return 0;
}

请注意,从文件中读取并写入不同的文件会更高效、更可靠。你可以尝试这个简单的过滤器并与上面的大文件代码进行比较:

#include <ctype.h>
#include <stdio.h>

int main() {
    int c;
    while ((c = getchar()) != EOF) {
        putchar(tolower(c));
    }
    return 0;
}