char *
str_replace(char *str, char *old, char *new) {
int i, count = 0;
int newlen = strlen(new);
int oldlen = strlen(old);
for (i = 0; str[i]; ++i)
if (strstr(&str[i], old) == &str[i])
++count, i += oldlen - 1;
char *ret = (char *) calloc(i + 1 + count * (newlen - oldlen), sizeof(char));
if (!ret) return;
i = 0;
while (*str)
if (strstr(str, old) == str)
strcpy(&ret[i], new),
i += newlen,
str += oldlen;
else
ret[i++] = *str++;
ret[i] = '\0';
return ret;
}
Refactorings
No refactoring yet !
R. Pate
June 27, 2009, June 27, 2009 06:15, permalink
My own weak first pass. (Tests included!)
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void
str_replace_inline(char* str, char const* old, char const* new) {
assert(str);
assert(new);
assert(old);
int const newlen = strlen(new);
int const oldlen = strlen(old);
assert(oldlen > 0);
assert(newlen <= oldlen);
char* next = str;
while (*str) {
if (strncmp(str, old, oldlen) == 0) {
strncpy(next, new, newlen);
next += newlen;
str += oldlen;
}
else {
*next++ = *str++;
}
}
while (*str) {
*next++ = *str++;
}
*next = '\0';
}
void
str_replace_malloc(char** str, int* str_size, char const* old, char const* new) {
assert(str != NULL);
assert(*str != NULL);
assert(str_size);
assert(new != NULL);
assert(old != NULL);
int const newlen = strlen(new);
int const oldlen = strlen(old);
assert(oldlen > 0);
if (newlen <= oldlen) {
str_replace_inline(*str, old, new);
}
else {
for (char* i = *str; *i; ++i) {
if (strncmp(i, old, oldlen) == 0) {
int new_str_size = (i - *str) + newlen * 2 + 100;
char* new_str = malloc(new_str_size);
if (!new_str) {
perror("malloc failed");
abort();
// or whatever you want
}
strncpy(new_str, *str, i - *str);
char* next = new_str + (i - *str);
strncpy(next, new, newlen);
next += newlen;
i += oldlen;
while (*i) {
if (strncmp(i, old, oldlen) == 0) {
strncpy(next, new, newlen);
next += newlen;
i += oldlen;
}
else {
*next++ = *i++;
}
if ((next - new_str) + newlen >= new_str_size) {
new_str_size *= 2;
new_str_size += newlen + 1;
char* new_new_str = malloc(new_str_size);
if (!new_new_str) {
perror("malloc failed");
abort();
// or whatever you want
}
strncpy(new_new_str, new_str, next - new_str);
next = new_new_str + (next - new_str);
free(new_str);
new_str = new_new_str;
}
}
*next = '\0';
*str = new_str;
*str_size = new_str_size;
return;
}
}
}
}
typedef struct {
int id;
char const* old;
char const* new;
char const* initial;
char const* expected;
} Test;
int main() {
Test tests[] = {
{__LINE__, "a", "b", "aaaa", "bbbb"},
{__LINE__, "a", "b", "abca", "bbcb"},
{__LINE__, "aa", "b", "aaaa", "bb"},
{__LINE__, "aa", "b", "aaab", "bab"},
{__LINE__, "a", "bb", "a", "bb"},
{__LINE__, "a", "bb", "aa", "bbbb"},
{__LINE__, "old", "newish", "blah blah old blah oldold blah", "blah blah newish blah newishnewish blah"},
{__LINE__, "old", "newish", "old blah old", "newish blah newish"},
};
for (Test* i = tests; i != tests + sizeof tests / sizeof *tests; ++i) {
int size = strlen(i->initial) + 1;
char* buf = malloc(size);
strcpy(buf, i->initial);
str_replace_malloc(&buf, &size, i->old, i->new);
if (strcmp(buf, i->expected) != 0) {
printf("failed test %d, expected '%s', got '%s'\n", i->id, i->expected, buf);
}
free(buf);
}
}
Eric Hoffman
April 7, 2010, April 07, 2010 05:52, permalink
A recursive implementation of the above. Has a max string length limiter, trades time for memory by allocating the minimum needed each pass and a few more test cases.
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void str_replace_inline(char* str, char const* old, char const* new) {
assert(str);
assert(new);
assert(old);
int const newlen = strlen(new);
int const oldlen = strlen(old);
assert(oldlen > 0);
assert(newlen <= oldlen);
char* next = str;
while (*str) {
if (strncmp(str, old, oldlen) == 0) {
strncpy(next, new, newlen);
next += newlen;
str += oldlen;
}
else {
*next++ = *str++;
}
}
while (*str) {
*next++ = *str++;
}
*next = '\0';
}
char* str_replace_malloc(char** str, unsigned int* str_size, char const* old, char const* new, const unsigned int maxlen) {
assert(str != NULL);
assert(*str != NULL);
assert(str_size);
assert(new != NULL);
assert(old != NULL);
int const newlen = strlen(new);
int const oldlen = strlen(old);
char* i;
assert(oldlen > 0);
if (newlen <= oldlen) {
str_replace_inline(*str, old, new);
}
else {
for (i = *str; *i; ++i) {
if (strncmp(i, old, oldlen) == 0) {
int new_str_size = *str_size + newlen - oldlen;
if (maxlen && new_str_size > maxlen) //0 for no max string length
return NULL;
char* new_str = malloc(new_str_size);
if (!new_str) {
perror("malloc failed");
abort();
}
strncpy(new_str, *str, i - *str); //copy before match
char* next = new_str + (i - *str);
strncpy(next, new, newlen); //append replacement
next += newlen;
i += oldlen;
strncpy (next, i, *str_size-(i-*str)); //copy after match
free (*str); //free old small source string
*str_size = new_str_size;
if (*next) //continue if not at end
str_replace_malloc(&new_str,str_size,old,new,maxlen);
*str = new_str;
}
}
}
return *str;
}
typedef struct {
int id;
char const* old;
char const* new;
char const* initial;
char const* expected;
} Test;
int main() {
Test* i;
Test tests[] = {
{__LINE__, "a", "b", "aaaa", "bbbb"},
{__LINE__, "a", "b", "abca", "bbcb"},
{__LINE__, "aa", "b", "aaaa", "bb"},
{__LINE__, "aa", "b", "aaab", "bab"},
{__LINE__, "aa", "a", "aaaa", "aa"},
{__LINE__, "a", "bb", "a", "bb"},
{__LINE__, "a", "bb", "aa", "bbbb"},
{__LINE__, "old", "newish", "blah blah old blah oldold blah", "blah blah newish blah newishnewish blah"},
{__LINE__, "old", "newish", "old blah old", "newish blah newish"},
{__LINE__, "a", "ba", "aa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaa"},
};
for (i = tests; i != tests + sizeof tests / sizeof *tests; ++i) {
unsigned int size = strlen(i->initial) + 1;
char* buf = malloc(size);
strcpy(buf, i->initial);
str_replace_malloc(&buf, &size, i->old, i->new, 40);
if (strcmp(buf, i->expected) != 0)
printf("failed test %d, expected '%s', got '%s'\n", i->id, i->expected, buf);
free(buf);
}
return 0;
}
Eric Hoffman
April 13, 2010, April 13, 2010 05:34, permalink
A second pass after using the code a bit.
char* strreplaceinline(char* str, char const* old, char const* new) {
assert(str);
assert(new);
assert(old);
char* strStart = str;
unsigned int const newlen = strlen(new);
unsigned int const oldlen = strlen(old);
assert(oldlen > 0);
assert(newlen <= oldlen);
char* next = str;
while (*str) {
if (strncmp(str, old, oldlen) == 0) {
strncpy(next, new, newlen);
next += newlen;
str += oldlen;
}
else {
*next++ = *str++;
}
}
while (*str) {
*next++ = *str++;
}
return strStart;
}
#define strreplace(a,b,c,d) strreplacen(a,b,c,d,0)
char* strreplacen(char* str, unsigned int* str_size, char const* old, char const* new, const unsigned int start) {
assert(str != NULL);
assert(str_size);
assert(new != NULL);
assert(old != NULL);
unsigned int const newlen = strlen(new);
unsigned int const oldlen = strlen(old);
char* i;
assert(oldlen > 0);
if (newlen <= oldlen) {
return strreplaceinline(str, old, new);
}
else {
for (i = str+start; *i; ++i) {
if (strncmp(i, old, oldlen) == 0) {
unsigned int new_str_size = *str_size + newlen - oldlen;
char* new_str = malloc(new_str_size);
if (!new_str) {
return NULL;
}
strncpy (new_str, str, i - str); //copy before match
char* next = new_str + (i - str);
strncpy (next, new, newlen); //append replacement
next += newlen;
i += oldlen;
strncpy (next, i, *str_size-(i-str)); //copy after match
if (start)
free (str); //free old small source string
*str_size = new_str_size;
if (*next) //continue if not at end
return strreplacen(new_str, str_size, old, new, next-new_str);
return new_str;
}
}
}
return str;
}
Pretty weak first pass at a character array substitution func