如何在命令行中交换指定文件中的字符(文件名和字符)?

How to swap characters in a file specified (file name and characters) in the command line?

我正在学习文件命令并尝试编写一些程序。我想在命令行中指定两个字符,每次找到第一个字符时,第二个字符将替换第一个字符。命令行输入为 [program_name].exe [file].txt [old_char] [new_char]。我遇到了有同样问题的 this thread,我试图通过查看答案来修复我的代码,但它创建了一个无限循环。

int main(int argc, char *argv[])
{
    FILE *fp;
    char *string = {"Hellx"};
    char c;
    if ((fp = fopen(argv[1], "w")) != NULL)
    {
        fputs(string, fp);
        printf("Successfully written.\n");
        fclose(fp);

        if ((fp = fopen(argv[1], "r+")) != NULL)
        {
            while ((c = fgetc(fp)) != EOF)
            {
                if (c == *argv[2])
                {
                    fseek(fp, ftell(fp) - 1, SEEK_SET);
                    fprintf(fp, "%c", *argv[3]);
                    printf("Successfully changed.\n");
                }
            }
            fclose(fp);
        }
        else
            printf("Error on opening!");
    }
    else
        printf("Error on writing!");
    return 0;
}

因此输出为:Helloelloelloelloelloelloello...,而它应该只是将 x 更改为 o。这段代码有什么问题?

fopen()man page 在讨论文件打开模式的部分中说

When the "r+", "w+", or "a+" access type is specified, both reading and writing are enabled (the file is said to be open for "update"). However, when you switch from reading to writing, the input operation must encounter an EOF marker. If there is no EOF, you must use an intervening call to a file positioning function. The file positioning functions are fsetpos, fseek, and rewind. When you switch from writing to reading, you must use an intervening call to either fflush or to a file positioning function.

(my bolding)

所以我建议添加

fflush(fp);

fprintf() 语句之后,因为不需要重新定位。


如前所述,您还应该将类型更改为

int c;

以便EOF -1可以与数据0xFF区分开来。

您的程序失败的原因已经在其他答案和评论中进行了广泛的辩论和解决。

Is there a short way to do this without "r+" and without fflush?

不止一个,这里举个文件打开读写的例子,这样就不用一直打开和关闭了,使用w+标志,就可以了如果文件不存在,也创建该文件:

#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE *fp;
    int c;
    char *string = {"Hellx"};
    if (argc > 3)
    {
        if ((fp = fopen(argv[1], "w+")) != NULL) // open to write and read
        {

            fputs(string, fp);
            printf("Successfully written.\n");           
            rewind(fp); // back to the beginning of the file
            while ((c = fgetc(fp)) != EOF)
            {
                if(c == argv[2][0]) // if the character exists...
                { 
                    fseek(fp, -1, SEEK_CUR);
                    fprintf(fp, "%c", argv[3][0]); // replace
                    printf("Successfully changed.\n");
                    fseek(fp, 0, SEEK_CUR);
                }
            }
            fclose(fp);
        }
        else
            fprintf(stderr, "Error on writing!");
    }
    else
        fprintf(stderr, "Too few arguments!");
}

脚注:

我同意 and ,更可靠的方法是使用两个不同的文件。

您的代码不起作用,因为您在从写入切换回读取时没有调用 fseek()rewind()。另请注意,您无需调用 ftell() 即可后退:您可以使用 -1LSEEK_CUR。对于这种文件修补到位,多用二进制打开文件也比较安全。

此外,fgetc() returns int 值不 适合 char。使用 int 类型 c 来可靠地检测 EOF。另请注意,成功时 fgetc() 返回的字节值是 unsigned char 的值,因此将其与 char 进行比较对于非 ASCII 字节可能会失败。

这是修改后的版本:

#include <stdio.h>

int main(int argc, char *argv[]) {
    FILE *fp;
    const char *string = { "Hellx" };

    if (argc < 4) {
        fprintf("missing command line arguments\n");
        return 1;
    }

    char *filename = argv[1];
    unsigned char c1 = argv[2][0];
    unsigned char c2 = argv[3][0];

    if ((fp = fopen(filename, "w")) == NULL) {
        fprintf(stderr, "cannot open %s for writing\n", filename);
        return 1;
    }
    fputs(string, fp);
    printf("Successfully written.\n");
    fclose(fp);
    
    if ((fp = fopen(filename, "rb+")) == NULL) {
        printf("Cannot reopen %s\n", filename);
        return 1;
    }
    while ((c = fgetc(fp)) != EOF) {
        if (c == c1) {
            fseek(fp, -1L, SEEK_CUR);
            fputc(c2, fp);
            fseek(fp, 0L, SEEK_CUR);
            printf("Successfully changed.\n");
        }
    }
    fclose(fp);
    return 0;
}