首页 > 解决方案 > 在文本文件中查找和替换特定字符串,而不创建新文件

问题描述

以下代码获取一个原始 txt 文件、一个要在文件中搜索的字符串以及一个替换原始字符串的新字符串。两个字符串的长度相同。此代码创建一个新文件(“new.txt”),将替换的文本写入那里,然后删除原始文件并将新文件重命名为原始文件。

问题是,我怎样才能使此代码功能相同但不创建新文件?换句话说,我只想修改原始文件本身。我对原始文件(f)尝试了 fprintf,但它的输出很奇怪。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 1024

int main(int argc, char *argv[])
{
    FILE *f = fopen(argv[1], "r+");
    FILE *f2 = fopen("new.txt", "a+");

    if(strlen(argv[2])!=strlen(argv[3]))
        printf("[%s] and [%s] have different lengths\n", argv[2], argv[3]);

    char write[MAX];
    int where;
    char* string = NULL;

    int len = strlen(argv[2]);
    int i=0;

    while(fgets(write, MAX, f)!=NULL)
    {
        if(NULL!=(string = strstr(write, argv[2])))
        {
            where = (int)(string - write);
            strncpy(write+where, argv[3], len);
        }
        fprintf(f2, "%s", write);
    }
    remove(argv[1]);
    rename("new.txt", argv[1]);
    return 0;
}

标签: cstringfile

解决方案


简短的回答是你真的不应该,但如果搜索和替换字符串的长度完全相同,这是可行的——但你不能在你写的字符数上犯任何错误——否则你会损坏文件。

在您的代码中,如果strlen不一样,您需要return或以其他方式处理错误,而不仅仅是输出该事实。

您已经f"r+"开头的 file-position-indicator 一样打开,strstr会告诉您是否找到要替换的单词,然后您需要做的就是将 file-positon-indicator 设置为返回的指针的位置strstr并在搜索词上写下替换字符,并在该行的其余部分重复。

需要特别注意保持文件位置指示符的偏移量。阅读后,指示器将超过最后一个字符fgets,因此您需要where - write - strlen(write)在代码中备份。(注意:偏移量是负数)

您可以使用fseek来回退和重置文件位置指示器,但您可能会更好地使用fgetpos来保存当前指示器位置并fsetpos恢复它,同时fseek只调用一次来定位指示器以进行替换。

将其完全放在一个简短的示例中,并使用buf, findandreplace代替writeand where,您可以执行以下操作:

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

#define MAXC 1024

int main (int argc, char **argv) {

    char buf[MAXC], *find, *replace;    /* read buf, find, replace pointers */
    size_t findlen;     /* length of find string */
    FILE *f = NULL;     /* file pointer */

    if (argc < 4 ) {    /* validate sufficient arguments given */
        fprintf (stderr, "error: insufficient input, "
                "usage: %s file find repl\n", argv[0]);
        return 1;
    }
    find = argv[2];     /* set find, replace & length */
    replace = argv[3];
    findlen = strlen (find);

    if (findlen != strlen (argv[3])) {  /* validate length the same */
        fprintf (stderr, "error find/replace lengths differ.\n");
        return 1;
    }

    if (!(f = fopen (argv[1], "r+"))) {  /* validate file open for reading+ */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, MAXC, f)) {  /* read each line into buf */
        char *findp = buf;          /* find pointer to search buf */
        /* 
         * buf length and search term split at end validation omitted.
         */
        while ((findp = strstr (findp, find))) {    /* while find found */
            fpos_t pos;         /* object to hold current file position */
            /* compute characters to backup (negative) */
            long backup = (long)(findp - buf) - strlen(buf);

            fgetpos (f, &pos);              /* save the current position */
            fseek (f, backup, SEEK_CUR);    /* backup */

            for (char *p = replace; *p; p++)
                fputc (*p, f);  /* overwrite char-by-char */

            fsetpos (f, &pos);  /* reset file position */

            findp += findlen;   /* advance beyond current find */
        }
    }

    if (fclose (f) == EOF)    /* validate close after write */
        perror ("fclose(f)");

    return 0;
}

示例输入文件

$ cat dat/dogfleas.txt
my dog has fleas
other dogs run away
my dog is an ichy dog

示例使用和生成的文件

$ ./bin/file_replace_in_place dat/dogfleas.txt dog cat

$ cat dat/dogfleas.txt
my cat has fleas
other cats run away
my cat is an ichy cat

在 shell 命令中没有双关语


推荐阅读