It doesn't seem clear to me what algorithm you are trying to follow, it all looks fishy to me. What's probably the simplest approach is:
- search for first occurrence of the "needle" (searched-for substring)
- copy the part before the first occurrence to the result buffer
- append the replacement string to the result buffer
- increment the
ppointer so it points just after the needle - GOTO 10
void str_replace(char *target, const char *needle, const char *replacement)
{
char buffer[1024] = { 0 };
char *insert_point = &buffer[0];
const char *tmp = target;
size_t needle_len = strlen(needle);
size_t repl_len = strlen(replacement);
while (1) {
const char *p = strstr(tmp, needle);
// walked past last occurrence of needle; copy remaining part
if (p == NULL) {
strcpy(insert_point, tmp);
break;
}
// copy part before needle
memcpy(insert_point, tmp, p - tmp);
insert_point += p - tmp;
// copy replacement string
memcpy(insert_point, replacement, repl_len);
insert_point += repl_len;
// adjust pointers, move on
tmp = p + needle_len;
}
// write altered string back to target
strcpy(target, buffer);
}
Warning: You also have to be careful about how you call your function. If the replacement string is larger than the needle, your modified string will be longer than the original one, so you have to make sure your original buffer is long enough to contain the modified string. E.g.:
char s[1024] = "marie has apples has";
str_replace(s, "has", "blabla");
Answer from The Paramagnetic Croissant on Stack OverflowIt doesn't seem clear to me what algorithm you are trying to follow, it all looks fishy to me. What's probably the simplest approach is:
- search for first occurrence of the "needle" (searched-for substring)
- copy the part before the first occurrence to the result buffer
- append the replacement string to the result buffer
- increment the
ppointer so it points just after the needle - GOTO 10
void str_replace(char *target, const char *needle, const char *replacement)
{
char buffer[1024] = { 0 };
char *insert_point = &buffer[0];
const char *tmp = target;
size_t needle_len = strlen(needle);
size_t repl_len = strlen(replacement);
while (1) {
const char *p = strstr(tmp, needle);
// walked past last occurrence of needle; copy remaining part
if (p == NULL) {
strcpy(insert_point, tmp);
break;
}
// copy part before needle
memcpy(insert_point, tmp, p - tmp);
insert_point += p - tmp;
// copy replacement string
memcpy(insert_point, replacement, repl_len);
insert_point += repl_len;
// adjust pointers, move on
tmp = p + needle_len;
}
// write altered string back to target
strcpy(target, buffer);
}
Warning: You also have to be careful about how you call your function. If the replacement string is larger than the needle, your modified string will be longer than the original one, so you have to make sure your original buffer is long enough to contain the modified string. E.g.:
char s[1024] = "marie has apples has";
str_replace(s, "has", "blabla");
For starters:
This line
strncpy(buffer, string, p-string);
not necessarily appends a 0-terminator to what had been copied to buffer.
The following line
strcat(buffer, replace);
however relies on buffer being 0-terminated.
As buffer had not been initialised and though the 0-terminator most likely misses the latter line may very well read beyond buffer's memory and with this invoke the infamous Undefined Behaviour.
The interface
There are a number of useful things to return. Let's list them in order of which is least surprising:
- The pointer to the start of the destination. This follows the standard library, but is least useful.
- The pointer to the end of the destination. This follows an alternative convention embodies in the
stpcpy()function and similar added to POSIX in 2008. - Number of characters produced / to produce. Generally only used if the sink can accept any number of characters, or the destinations size is also passed.
countOccurrences()seems your flawed attempt to begin implementing the alternative approach to sizing the buffer. - Return a count of replacements done. Degenerates to boolean if maximum number of replacements is 1.
- Try to combine bool (not count) with one of the other options.
strstr()does it, arguable returning a pointer to the terminator might have been better.
None of those options reflects your choice.
Provide some way to properly size the destination buffer. As a bonus,
replaceNAndAllocate()becomes fairly trivial.
The algorithm
replaceN()'s use ofreplace()results in a standard Shlemiel The Painter-algorithm. It should be linear, but ends up quadratic.countOccurrences()can fail if some proper suffix (neither empty nor the full string) is a prefix of the needle, specifically if the resultant string is found. Skip over the full match found, not the first character.
Implementation
Yes,
strncpy()(which is not actually a string-function) will behave identically tomemcpy()if the firstn - 1bytes are non-null. It is still inefficient and false advertisement though.&pointer[offset_calculation]might not be longer thanpointer + offset_calculation, but it is a bit less idiomatic. You decide whether it should be changed.replaceN()should be the one to implement directly,replace()being a convenience-function. The overhead is trivial for the latter, and fixes the current problem with its runtime.Most of the time, you use
pointerdirectly, but sometimes you usepointer != NULL. Fix the inconsistency, preferably in favor of the former, which is more idiomatic and shorter.countOccurrences()andallocateBuffer()seem to be internal helpers. Mark themstaticso they are not exported.Your test-program fails to free the buffer
replaceAllAndAllocate()allocates.
First impression is pretty good - care taken with exception conditions and thought to the interface.
Headers: I find it helpful to have a consistent order for headers, to quickly see if the header I need is already included. For this, I group the Standard Library headers in alphabetical order. You might find that helpful too.
char *ptr = strstr(src, orig); // First match
I'd write const *ptr there, to avoid accidents.
strncpy(dest, src, offset); // Copy everything before match
Probably memcpy is more appropriate here, given we know that src doesn't end before src + offset.
strcpy(&dest[offset], new); // Copy replacement strcpy(&dest[offset + strlen(new)], &src[offset + origlen]); // Copy rest
It's strange to index an array, only to extract a pointer. I'd write those as:
strcpy(dest + offset, new); // Copy replacement strcpy(dest + offset + strlen(new), src + offset + origlen); // Copy rest
Arguably, we could measure strlen(new) first, then use memcpy for the first of those.
If countOccurrences() and allocateBuffer() aren't intended to be part of the public interface, declare them with internal linkage (static).
The test program is an optimist. If you've taken the care to safely return a null pointer from the function, the caller ought to have the decency to set a good example by testing the value rather than blindly passing it on!
The optimizer should eliminate most of the local variables. The tmp pointer is there to make sure strcpy doesn't have to walk the string to find the null. tmp points to the end of result after each call. (See Shlemiel the painter's algorithm for why strcpy can be annoying.)
// You must free the result if result is non-NULL.
char *str_replace(char *orig, char *rep, char *with) {
char *result; // the return string
char *ins; // the next insert point
char *tmp; // varies
int len_rep; // length of rep (the string to remove)
int len_with; // length of with (the string to replace rep with)
int len_front; // distance between rep and end of last rep
int count; // number of replacements
// sanity checks and initialization
if (!orig || !rep)
return NULL;
len_rep = strlen(rep);
if (len_rep == 0)
return NULL; // empty rep causes infinite loop during count
if (!with)
with = "";
len_with = strlen(with);
// count the number of replacements needed
ins = orig;
for (count = 0; (tmp = strstr(ins, rep)); ++count) {
ins = tmp + len_rep;
}
tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
if (!result)
return NULL;
// first time through the loop, all the variable are set correctly
// from here on,
// tmp points to the end of the result string
// ins points to the next occurrence of rep in orig
// orig points to the remainder of orig after "end of rep"
while (count--) {
ins = strstr(orig, rep);
len_front = ins - orig;
tmp = strncpy(tmp, orig, len_front) + len_front;
tmp = strcpy(tmp, with) + len_with;
orig += len_front + len_rep; // move to next "end of rep"
}
strcpy(tmp, orig);
return result;
}
This is not provided in the standard C library because, given only a char* you can't increase the memory allocated to the string if the replacement string is longer than the string being replaced.
You can do this using std::string more easily, but even there, no single function will do it for you.
There is no direct function to do that. You have to write something like this, using strchr:
char* replace_char(char* str, char find, char replace){
char *current_pos = strchr(str,find);
while (current_pos) {
*current_pos = replace;
current_pos = strchr(current_pos,find);
}
return str;
}
For whole strings, I refer to this answered question
There are not such functions in the standard libraries.
You can easily roll your own using strchr for replacing one single char, or strstr to replace a substring (the latter will be slightly more complex).
int replacechar(char *str, char orig, char rep) {
char *ix = str;
int n = 0;
while((ix = strchr(ix, orig)) != NULL) {
*ix++ = rep;
n++;
}
return n;
}
This one returns the number of chars replaced and is even immune to replacing a char by itself
Your code might crash if the there is no input.
Use the standard function strstr(). Why to reinvent the wheel? You can also improve the complexity of the code. See this link:
https://www.geeksforgeeks.org/frequency-substring-string/
I don't have permission to comment yet so I have to answer you here.
A char variable is encoded with 8 bits, so it can be 256 values [0,255].
Printable characters can be found in the ASCII table where you can see that the character a is equal to the decimal value 97.
If you consider characters as decimal value, I am sure you can figure out by yourself how to pass from a (97) to A (65), so you will be able to perform the same operation for any character.