I wrote this code as an answer to a question. But I'd like you to have a look at it. This post is basically a copy of the answer I posted.
The code does no error checking. It assumes that the output buffer exists and is big enough and that src
, orig
and new
are valid strings. The headers needed:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
The main replace function
// Replace the first occurrence of orig in src with new and write result to dest
// Return pointer to first character after the end of first match in src and
// NULL if no match
const char *replace(char *dest, const char *src,
const char *orig, const char *new) {
char *ptr = strstr(src, orig); // First match
// If no match, we want dest to contain a copy of src
if(!ptr) {
strcpy(dest, src);
return NULL;
}
const ptrdiff_t offset = ptr - src; // Calculate offset
const int origlen = strlen(orig);
strncpy(dest, src, offset); // Copy everything before match
strcpy(&dest[offset], new); // Copy replacement
strcpy(&dest[offset + strlen(new)], &src[offset + origlen]); // Copy rest
return src + offset + origlen;
}
Then we just add a function to handle N occurrences
// Replace maximum n occurrences. Stops when no more matches.
// Returns number of replacements
size_t replaceN(char *dest, const char *src, const char *orig,
const char *new, size_t n) {
size_t ret = 0;
// Maybe an unnecessary optimization to avoid multiple calls in
// loop, but it also adds clarity
const int newlen = strlen(new);
const int origlen = strlen(orig);
do {
const char *ptr = replace(dest, src, orig, new); // Replace
if(!ptr) return ret; // Quit if no more matches
// Length of the part of src before first match
const ptrdiff_t offset = ptr - src;
src = ptr; // Move src past what we have already copied.
ret++;
dest += offset - origlen + newlen; // Advance pointer to dest to the end
} while(n > ret);
return ret;
}
And a simple wrapper for all occurrences. Note that it's safe to use SIZE_MAX
here, because there will never be more occurrences than that.
// Replace all. Returns the number of replacements, because why not?
size_t replaceAll(char *dest, const char *src, const char *orig, const char *new) {
return replaceN(dest, src, orig, new, SIZE_MAX);
}
It's easy to write a wrapper for the allocation. We borrow some code from https://stackoverflow.com/q/9052490/6699433
size_t countOccurrences(const char *str, const char *substr) {
size_t count = 0;
while((str = strstr(str, substr)) != NULL) {
count++;
str++; // We're standing at the match, so we need to advance
}
return count;
}
Then some code to calculate size and allocate a buffer
// Allocate a buffer big enough to hold src with n replacements
// of orig to new
char *allocateBuffer(const char *src, const char *orig,
const char *new, size_t n) {
return malloc(strlen(src) +
n * (strlen(new) - strlen(orig)) +
1 // Remember the zero terminator
);
}
And the two final functions
// Allocates a buffer and replaces max n occurrences of orig with new and
// writes it to the allocated buffer.
// Returns the buffer and NULL if allocation failed
char *replaceNAndAllocate(const char *src, const char *orig,
const char *new, size_t n) {
const size_t count = countOccurrences(src, orig);
n = n < count ? n : count; // Min of n and count
char *buf = allocateBuffer(src, orig, new, n);
if(!buf) return NULL;
replaceN(buf, src, orig, new, n);
return buf;
}
// Allocates a buffer and replaces all occurrences of orig with new and
// writes it to the allocated buffer.
// Returns the buffer and NULL if allocation failed
char *replaceAllAndAllocate(const char *src, const char *orig, const char *new) {
return replaceNAndAllocate(src, orig, new, SIZE_MAX);
}
And finally, a simple main
with a test with multiple occurrences and with original string being a substring of replacement string:
int main(void) {
char src[] = "!!!asdf!!!asdf!!!asdf!!!asdf!!!";
char orig[] = "asdf";
char new[] = "asdfaaaaaaaaasdf";
puts(replaceAllAndAllocate(src, orig, new));
}
No warnings with -Wall -Wextra -pedantic
and the output is:
$ ./a.out
!!!asdfaaaaaaaaasdf!!!asdfaaaaaaaaasdf!!!asdfaaaaaaaaasdf!!!asdfaaaaaaaaasdf!!!