F1e3ab214a976a39cfd713bc93deb10f

Pretty weak first pass at a character array substitution func

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 !

48ccd8d56bce9ebd25c1720c13e9d442

R. Pate

June 27, 2009, June 27, 2009 06:15, permalink

No rating. Login to rate!

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);
  }
}
Bc359a8417136e2a96298b66b4745e0b

Eric Hoffman

April 7, 2010, April 07, 2010 05:52, permalink

No rating. Login to rate!

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;
}
D41d8cd98f00b204e9800998ecf8427e

Eric Hoffman

April 13, 2010, April 13, 2010 05:34, permalink

No rating. Login to rate!

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;
}

Your refactoring





Format Copy from initial code

or Cancel