c - 在文本文件中查找和替换特定字符串,而不创建新文件
问题描述
以下代码获取一个原始 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;
}
解决方案
简短的回答是你真的不应该,但如果搜索和替换字符串的长度完全相同,这是可行的——但你不能在你写的字符数上犯任何错误——否则你会损坏文件。
在您的代码中,如果strlen
不一样,您需要return
或以其他方式处理错误,而不仅仅是输出该事实。
您已经f
像"r+"
开头的 file-position-indicator 一样打开,strstr
会告诉您是否找到要替换的单词,然后您需要做的就是将 file-positon-indicator 设置为返回的指针的位置strstr
并在搜索词上写下替换字符,并在该行的其余部分重复。
需要特别注意保持文件位置指示符的偏移量。阅读后,指示器将超过最后一个字符fgets
,因此您需要where - write - strlen(write)
在代码中备份。(注意:偏移量是负数)
您可以使用fseek
来回退和重置文件位置指示器,但您可能会更好地使用fgetpos
来保存当前指示器位置并fsetpos
恢复它,同时fseek
只调用一次来定位指示器以进行替换。
将其完全放在一个简短的示例中,并使用buf
, find
andreplace
代替write
and 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 命令中没有双关语)
推荐阅读
- c# - 当没有选择“..选择房间...”之类的项目时如何在组合框中显示文本
- python - 多元分布的等高线图
- dart - 如何使用 PaginatedDataTable 在列、字段和行上正确实现点击事件
- javascript - 如何获取变量的值
- mobx - Mobx:观察对象的反应不起作用
- timestamp - 监控时间戳检查:如何在每次检查时运行自定义脚本?
- graphql - 使用 NEXT.js 和 Apollo 客户端 2 配置订阅
- c# - 为什么我在 React 应用程序中的 Fetch URL 没有改变?
- javascript - 嵌套对象中的空检查 - Javascript
- python - 循环中的通信()失败