如何用 C 语言替换文本文件中的 string/phrase
How to replace a string/phrase in a text file in C
如何在简单的 C 程序中替换文本文件中给定字符串的每个实例?
有人会认为这是一个很容易用 Google 搜索到的问题,是吗?
在询问时,SanFoundry, TutorialsPoint, GeeksforGeeks, w3resource, Codeforwin, a blog, GeeksforGeeks (twice) and even Code Review SE 有严重损坏的帖子,可能会在截止日期前向绝望的人提供功能失调的代码。这些是令人无法接受的糟糕答案,除了 C.R。 SE,它清楚地解决了它的局限性。
因此必须在 Stack Overflow 等可靠平台上以问答形式解决此问题。
此问答是为了避免将来的 C beginners/intermediates 在搜索如何执行此操作时出现的废话。
如果您是初学者,请阅读算法并尝试自己实现它,而不是仅仅复制代码。
免责声明:该算法将替换sandwich
中的sand
。如果这是一个 不良影响 (取决于您的情况),我的 会缓解这种情况。
Algorithm/Steps :
- 提醒用户备份他们的文件。
- 获取文件名.
- Initialise : 打开用户输入的文件进行读取,打开临时文件进行写入,测试这些文件中的任何错误 openings.If 成功,获取目标字符串和它的替代品。
- Parse : 遍历文件并记录其中的行数,然后
rewind()
fptr.
- 循环 1:保持在行数范围内,从文件中获取一行。
- If:使用
strstr()
判断行是否包含目标字符串。如果是,则开始循环 2。
- 循环 2:当在行中找到目标字符串的任何实例时,写入 tmp
a)
行的所有字符直到最后一个匹配,并且 b)
要替换的词。将最后一个匹配项的引用移动到当前匹配项的位置 + 目标字符串的长度。
- 在下一行之前:循环 2 的一次迭代完成后,写入从最后一场比赛到
EOL
的 any/all 个字符。移至循环 1 的下一次迭代。
- Else:如果在
5
处,发现该行没有目标字符串实例,直接写入文件。
- Cleanup:
fclose()
tmp and original file.Check if fclose(tmp)
was successful- if not, throw an error and remove temp file,并完成(因为这可能表明 tmp 未按预期写入,例如,如果磁盘已满)。 Else rename()
tmp file to filename of original,如果失败(如在 Windows 系统中,文件不能重命名为已经存在的文件名),remove()
original 并重试,即使那样失败,抛出错误并放弃。
- 结束:(可选)如果没有失败则向用户打印一条'success'消息。
代码:
#include <stdio.h>
#include <string.h>
#define MAX_W 501
#define MAX_L 2001
#define MAX_F 261
void eat(void); //clears stdin
int main(){
printf("\nKeep a backup of your file in case of undesirable effects.\n");
char frep[MAX_F]; printf("\n Filename : "); scanf("%260[^\n]",frep);eat();
// stores fname in frep[], clears stdin
FILE * rep = fopen(frep,"r");FILE * tmp = fopen("Temp.Ctt","w");
// opens file for reading and tmp for writing
if(rep==NULL||tmp==NULL){
//NULL is returned if fopen fails.
perror("\nError ");
}
else{
char target[MAX_F]; printf("\n Target : "); scanf("%500[^\n]",target);eat(); // gets target word
char replace[MAX_F]; printf("\n Replacement : "); scanf("%500[^\n]",replace);eat();// gets its replacement
long long lncnt=1;
while(1){ //parsing file to find number of lines.
int test = fgetc(rep);
if(test==EOF)
break;
else if(test=='\n'||test=='[=10=]')
lncnt++;
}
rewind(rep);
//brings pointer back to beginning of file
char line[MAX_L], *p; int i;
// line buffer, pointer to position of match, counter i; pointer Last Writing Point (initially points to first element of line[] )
for(i=1;i<lncnt;i++){
//while within no. of lines in file
fgets(line,MAX_L,rep);char * LWP = &line[0];
//takes line from file 'rep' and stores in line[]
while((p=strstr(LWP,target))!=NULL){
// while any matches exist from last writing point
int x = p-LWP;
// no. of chars between Last Writing Point and match.
fprintf(tmp,"%.*s",x,LWP);//writes all chars between last match and current match
fputs(replace,tmp);
LWP = p + strlen(target);
// moves the LWP forward by length of target, so that it points to next valid character.
}
fputs(LWP,tmp);
}
fclose(rep); int chk =fclose(tmp);
if(chk==EOF){
remove("Temp.Ctt"); perror("\nFailed ");
}
else{
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove(frep);
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove("Temp.Ctt"); perror("\nFailed ");
}
}
}
}
return 0;
}
void eat()
{
int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
如果还有任何疑问,请在下方发表评论。
- 从文件中读取一行到缓冲区
- 使用 strstr() 搜索下一次出现的“字符串”。
- 如果找到,print/write 找到“字符串”之前的所有字节
- Print/write 替换字符串。
- 跳过缓冲区的 strlen("the sting") 字节,并在 2
处重复
- 打印缓冲区的剩余部分。
- 重复 1
#define LINEBUF_LEN 2001
int replace(FILE *in, FILE *out, const char *str, const char *replace) {
size_t strl;
char line[LINEBUF_LEN];
strl = strlen(str);
while (fgets(line, sizeof(line), in)) {
char *y, *x = line;
while ((y = strstr(x, str))) {
fwrite(x, 1, y - x, out);
fputs(replace, out);
x = y + strl;
}
fputs(x, out);
}
return !feof(in);
}
我的 取代了 'phrases' 或 'strings' 最技术和最严格的词义。但是,正如声明的那样,它还将替换目标 的一部分另一个 string/word/phrase - 例如,'the'在“他们”中。
如果您想替换“Bob's”中的“Bob”,那就是您的选择。但是,除非小心使用,否则它可能太强大并且具有破坏性。
此答案替换 'words'(单词 = 十进制值 32 到 126 的 ASCII 字符字符串,以任何白色 space 字符结尾)。即,只有 word 'is' 被改变,而不是 'This'.
里面的 'is'
Algorithm/Steps :
0. , 1. & 2. 与第一个答案相同。
3. 无限循环:
i) 从文件中获取一个字符。
ii) 如果字符== EOF,中断循环。
iii) 否则如果字符是白色space字符,直接写入tmp文件。
iv) 否则取出单词,并将其放入缓冲区。使用 strcmp()
,比较缓冲区和目标。
v) 如果单词相同,使用strcpy()
将替换移动到缓冲区。
vi) 将缓冲区写入文件。
4. 和 5. 与原始答案的 9. 和 10. 相同。
代码:
#include <stdio.h>
#include <string.h>
#define MAX_W 501
#define MAX_F 261
void eat(void); //clears stdin
int main(){
printf("\nKeep a backup of your file in case of undesirable effects.\n");
char frep[MAX_F]; printf("\n Filename : "); scanf("%260[^\n]",frep);eat(); // stores fname in frep[], clears stdin
FILE * rep = fopen(frep,"r");FILE * tmp = fopen("Temp.Ctt","w");// opens file for reading and tmp for writing
if(rep==NULL||tmp==NULL){
// if files could not be opened
perror("\nError ");
}
else{
char target[MAX_W]; printf("\n Target : "); scanf("%500s",target);eat(); // gets target word
char replace[MAX_W]; printf("\n Replacement : "); scanf("%500[^\n]",replace);eat();// gets its replacement
while(1){
int ch = fgetc(rep);
if(ch==EOF)
break;
else if(ch==' '||ch=='\t'||ch=='\n'||ch == '\r')
fputc(ch,tmp);// directly write whitespace chars
else{
char buffer[MAX_W];
fseek(rep,-1,SEEK_CUR);
// move FILE pointer 1 byte back to read entire word, not from 2nd char onwards
fscanf(rep,"%500s",buffer);
if(strcmp(buffer,target)==0)
strcpy(buffer,replace);
fprintf(tmp,"%s",buffer);
}
}
fclose(rep); int chk =fclose(tmp);
if(chk==EOF){
remove("Temp.Ctt"); perror("\nFailed ");
}
else{
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove(frep);
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove("Temp.Ctt"); perror("\nFailed ");
}
}
}
}
return 0;
}
void eat()
{
int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
您可以编写一个过滤器在 stdin
上执行此替换,将修改后的数据写入 stdout
。您将创建一个修改后的文件,然后删除原始文件并将修改后的文件重命名或复制到原始文件名。
必须小心替换字符串,即使它出现在很长的行中,可能被 fgets()
拆分。
如果原始文件包含空字节,读取带有 fgets()
的行也将无法正常工作。一次读取一行也不允许替换包含嵌入换行符的字符串。
这是一个使用缓冲区并避免这些陷阱的版本。它采用目标字符串和可选的替换字符串作为命令行参数:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buffer[4096];
size_t len1, len2, pos, nread;
const char *str1, *str2;
if (argc < 2) {
fprintf(stderr, "usage: %s STRING [REPLACEMENT]\n", argv[0]);
return 1;
}
str1 = argv[1]; // string to search and replace
len1 = strlen(str1);
str2 = ""; // default replacement is empty
len2 = 0;
if (len1 >= sizeof(buffer)) {
fprintf(stderr, "%s: STRING too long\n", argv[0]);
return 1;
}
if (len1 == 0) { // special case empty string
len1 = len2 = 1; // replace 0 byte with itself
} else
if (argc > 2) {
str2 = argv[2];
len2 = strlen(str2);
}
pos = 0;
while ((nread = fread(buffer + pos, 1, sizeof(buffer) - pos, stdin)) != 0) {
size_t start = 0, i = 0, end = pos + nread;
while (i + len1 <= end) {
if (buffer[i] == *str1 && !memcmp(buffer + i, str1, len1)) {
fwrite(buffer + start, 1, i - start, stdout);
fwrite(str2, 1, len2, stdout);
start = i += len1;
} else {
i++;
}
}
fwrite(buffer + start, 1, i - start, stdout);
memmove(buffer, buffer + i, end - i);
pos = end - i;
}
fwrite(buffer, 1, pos, stdout);
return 0;
}
如何在简单的 C 程序中替换文本文件中给定字符串的每个实例?
有人会认为这是一个很容易用 Google 搜索到的问题,是吗?
在询问时,SanFoundry, TutorialsPoint, GeeksforGeeks, w3resource, Codeforwin, a blog, GeeksforGeeks (twice) and even Code Review SE 有严重损坏的帖子,可能会在截止日期前向绝望的人提供功能失调的代码。这些是令人无法接受的糟糕答案,除了 C.R。 SE,它清楚地解决了它的局限性。
因此必须在 Stack Overflow 等可靠平台上以问答形式解决此问题。
此问答是为了避免将来的 C beginners/intermediates 在搜索如何执行此操作时出现的废话。
如果您是初学者,请阅读算法并尝试自己实现它,而不是仅仅复制代码。
免责声明:该算法将替换sandwich
中的sand
。如果这是一个 不良影响 (取决于您的情况),我的
Algorithm/Steps :
- 提醒用户备份他们的文件。
- 获取文件名.
- Initialise : 打开用户输入的文件进行读取,打开临时文件进行写入,测试这些文件中的任何错误 openings.If 成功,获取目标字符串和它的替代品。
- Parse : 遍历文件并记录其中的行数,然后
rewind()
fptr. - 循环 1:保持在行数范围内,从文件中获取一行。
- If:使用
strstr()
判断行是否包含目标字符串。如果是,则开始循环 2。 - 循环 2:当在行中找到目标字符串的任何实例时,写入 tmp
a)
行的所有字符直到最后一个匹配,并且b)
要替换的词。将最后一个匹配项的引用移动到当前匹配项的位置 + 目标字符串的长度。 - 在下一行之前:循环 2 的一次迭代完成后,写入从最后一场比赛到
EOL
的 any/all 个字符。移至循环 1 的下一次迭代。 - Else:如果在
5
处,发现该行没有目标字符串实例,直接写入文件。 - Cleanup:
fclose()
tmp and original file.Check iffclose(tmp)
was successful- if not, throw an error and remove temp file,并完成(因为这可能表明 tmp 未按预期写入,例如,如果磁盘已满)。 Elserename()
tmp file to filename of original,如果失败(如在 Windows 系统中,文件不能重命名为已经存在的文件名),remove()
original 并重试,即使那样失败,抛出错误并放弃。 - 结束:(可选)如果没有失败则向用户打印一条'success'消息。
代码:
#include <stdio.h>
#include <string.h>
#define MAX_W 501
#define MAX_L 2001
#define MAX_F 261
void eat(void); //clears stdin
int main(){
printf("\nKeep a backup of your file in case of undesirable effects.\n");
char frep[MAX_F]; printf("\n Filename : "); scanf("%260[^\n]",frep);eat();
// stores fname in frep[], clears stdin
FILE * rep = fopen(frep,"r");FILE * tmp = fopen("Temp.Ctt","w");
// opens file for reading and tmp for writing
if(rep==NULL||tmp==NULL){
//NULL is returned if fopen fails.
perror("\nError ");
}
else{
char target[MAX_F]; printf("\n Target : "); scanf("%500[^\n]",target);eat(); // gets target word
char replace[MAX_F]; printf("\n Replacement : "); scanf("%500[^\n]",replace);eat();// gets its replacement
long long lncnt=1;
while(1){ //parsing file to find number of lines.
int test = fgetc(rep);
if(test==EOF)
break;
else if(test=='\n'||test=='[=10=]')
lncnt++;
}
rewind(rep);
//brings pointer back to beginning of file
char line[MAX_L], *p; int i;
// line buffer, pointer to position of match, counter i; pointer Last Writing Point (initially points to first element of line[] )
for(i=1;i<lncnt;i++){
//while within no. of lines in file
fgets(line,MAX_L,rep);char * LWP = &line[0];
//takes line from file 'rep' and stores in line[]
while((p=strstr(LWP,target))!=NULL){
// while any matches exist from last writing point
int x = p-LWP;
// no. of chars between Last Writing Point and match.
fprintf(tmp,"%.*s",x,LWP);//writes all chars between last match and current match
fputs(replace,tmp);
LWP = p + strlen(target);
// moves the LWP forward by length of target, so that it points to next valid character.
}
fputs(LWP,tmp);
}
fclose(rep); int chk =fclose(tmp);
if(chk==EOF){
remove("Temp.Ctt"); perror("\nFailed ");
}
else{
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove(frep);
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove("Temp.Ctt"); perror("\nFailed ");
}
}
}
}
return 0;
}
void eat()
{
int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
如果还有任何疑问,请在下方发表评论。
- 从文件中读取一行到缓冲区
- 使用 strstr() 搜索下一次出现的“字符串”。
- 如果找到,print/write 找到“字符串”之前的所有字节
- Print/write 替换字符串。
- 跳过缓冲区的 strlen("the sting") 字节,并在 2 处重复
- 打印缓冲区的剩余部分。
- 重复 1
#define LINEBUF_LEN 2001
int replace(FILE *in, FILE *out, const char *str, const char *replace) {
size_t strl;
char line[LINEBUF_LEN];
strl = strlen(str);
while (fgets(line, sizeof(line), in)) {
char *y, *x = line;
while ((y = strstr(x, str))) {
fwrite(x, 1, y - x, out);
fputs(replace, out);
x = y + strl;
}
fputs(x, out);
}
return !feof(in);
}
我的
如果您想替换“Bob's”中的“Bob”,那就是您的选择。但是,除非小心使用,否则它可能太强大并且具有破坏性。
此答案替换 'words'(单词 = 十进制值 32 到 126 的 ASCII 字符字符串,以任何白色 space 字符结尾)。即,只有 word 'is' 被改变,而不是 'This'.
里面的 'is'Algorithm/Steps :
0. , 1. & 2. 与第一个答案相同。
3. 无限循环:
i) 从文件中获取一个字符。
ii) 如果字符== EOF,中断循环。
iii) 否则如果字符是白色space字符,直接写入tmp文件。
iv) 否则取出单词,并将其放入缓冲区。使用 strcmp()
,比较缓冲区和目标。
v) 如果单词相同,使用strcpy()
将替换移动到缓冲区。
vi) 将缓冲区写入文件。
4. 和 5. 与原始答案的 9. 和 10. 相同。
代码:
#include <stdio.h>
#include <string.h>
#define MAX_W 501
#define MAX_F 261
void eat(void); //clears stdin
int main(){
printf("\nKeep a backup of your file in case of undesirable effects.\n");
char frep[MAX_F]; printf("\n Filename : "); scanf("%260[^\n]",frep);eat(); // stores fname in frep[], clears stdin
FILE * rep = fopen(frep,"r");FILE * tmp = fopen("Temp.Ctt","w");// opens file for reading and tmp for writing
if(rep==NULL||tmp==NULL){
// if files could not be opened
perror("\nError ");
}
else{
char target[MAX_W]; printf("\n Target : "); scanf("%500s",target);eat(); // gets target word
char replace[MAX_W]; printf("\n Replacement : "); scanf("%500[^\n]",replace);eat();// gets its replacement
while(1){
int ch = fgetc(rep);
if(ch==EOF)
break;
else if(ch==' '||ch=='\t'||ch=='\n'||ch == '\r')
fputc(ch,tmp);// directly write whitespace chars
else{
char buffer[MAX_W];
fseek(rep,-1,SEEK_CUR);
// move FILE pointer 1 byte back to read entire word, not from 2nd char onwards
fscanf(rep,"%500s",buffer);
if(strcmp(buffer,target)==0)
strcpy(buffer,replace);
fprintf(tmp,"%s",buffer);
}
}
fclose(rep); int chk =fclose(tmp);
if(chk==EOF){
remove("Temp.Ctt"); perror("\nFailed ");
}
else{
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove(frep);
if(rename("Temp.Ctt",frep)==0)
printf("\nSucess.\n\nReplaced any instances of \"%s\" with \"%s\".\n",target,replace);
else{
remove("Temp.Ctt"); perror("\nFailed ");
}
}
}
}
return 0;
}
void eat()
{
int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
您可以编写一个过滤器在 stdin
上执行此替换,将修改后的数据写入 stdout
。您将创建一个修改后的文件,然后删除原始文件并将修改后的文件重命名或复制到原始文件名。
必须小心替换字符串,即使它出现在很长的行中,可能被 fgets()
拆分。
如果原始文件包含空字节,读取带有 fgets()
的行也将无法正常工作。一次读取一行也不允许替换包含嵌入换行符的字符串。
这是一个使用缓冲区并避免这些陷阱的版本。它采用目标字符串和可选的替换字符串作为命令行参数:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buffer[4096];
size_t len1, len2, pos, nread;
const char *str1, *str2;
if (argc < 2) {
fprintf(stderr, "usage: %s STRING [REPLACEMENT]\n", argv[0]);
return 1;
}
str1 = argv[1]; // string to search and replace
len1 = strlen(str1);
str2 = ""; // default replacement is empty
len2 = 0;
if (len1 >= sizeof(buffer)) {
fprintf(stderr, "%s: STRING too long\n", argv[0]);
return 1;
}
if (len1 == 0) { // special case empty string
len1 = len2 = 1; // replace 0 byte with itself
} else
if (argc > 2) {
str2 = argv[2];
len2 = strlen(str2);
}
pos = 0;
while ((nread = fread(buffer + pos, 1, sizeof(buffer) - pos, stdin)) != 0) {
size_t start = 0, i = 0, end = pos + nread;
while (i + len1 <= end) {
if (buffer[i] == *str1 && !memcmp(buffer + i, str1, len1)) {
fwrite(buffer + start, 1, i - start, stdout);
fwrite(str2, 1, len2, stdout);
start = i += len1;
} else {
i++;
}
}
fwrite(buffer + start, 1, i - start, stdout);
memmove(buffer, buffer + i, end - i);
pos = end - i;
}
fwrite(buffer, 1, pos, stdout);
return 0;
}