Reputation: 21
The following code gets a original txt file, a string to search in the file, and a new string to replace the original one. Two strings' length would be same. This code creates a new file ("new.txt"), writes the replaced text there, and then remove the original file & rename the new one as the original.
The question is, how can I make this code to function the same but do not create a new file? In other words, I want to just modify the original file itself. I tried fprintf to the original file (f), but its output was weird.
#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;
}
Upvotes: 1
Views: 1196
Reputation: 84559
The short answer is you really shouldn't, but it is doable if the search and replace strings are exactly the same length -- but you cannot make any mistakes in the number of characters you write -- or you will corrupt the file.
In your code, if the strlen
isn't the same, you need to return
or otherwise handle the error, not just output that fact.
You already have f
open as "r+"
with the file-position-indicator at the beginning, strstr
will tell you if you have found the word to replace, then all you need to do is set the file-positon-indicator to the location of the pointer returned by strstr
and write the replacement characters over the search term, and repeat for the remainder of the line.
Keeping the offset for the file position indicater straight will need careful attention. After your read, the indicator will be one past the last character read by fgets
, so you will need to backup by where - write - strlen(write)
in your code. (note: the offset is intended to be negative)
You can use fseek
to rewind and reset the file-position-indicator, but you are probably better served using fgetpos
to save the current indicator position and fsetpos
to restore it, while calling fseek
only once to position to indicator for the replacement.
Putting it altogether in a short example, and using buf
, find
and replace
, instead of write
and where
, you could do something like the following:
#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;
}
Example Input File
$ cat dat/dogfleas.txt
my dog has fleas
other dogs run away
my dog is an ichy dog
Example Use and Resulting File
$ ./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
(no pun in shell command intended)
Upvotes: 3