Strings: Search and Replace

Dave Sinkula 0 Tallied Votes 1K Views Share

Finding some text and replacing it with new text within a C string can be a little trickier than expected. Here is what I had come up with one day.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Description:
 *   Find and replace text within a string.
 *
 * Parameters:
 *   src  (in) - pointer to source string
 *   from (in) - pointer to search text
 *   to   (in) - pointer to replacement text
 *
 * Returns:
 *   Returns a pointer to dynamically-allocated memory containing string
 *   with occurences of the text pointed to by 'from' replaced by with the
 *   text pointed to by 'to'.
 */
char *replace(const char *src, const char *from, const char *to)
{
   /*
    * Find out the lengths of the source string, text to replace, and
    * the replacement text.
    */
   size_t size    = strlen(src) + 1;
   size_t fromlen = strlen(from);
   size_t tolen   = strlen(to);
   /*
    * Allocate the first chunk with enough for the original string.
    */
   char *value = malloc(size);
   /*
    * We need to return 'value', so let's make a copy to mess around with.
    */
   char *dst = value;
   /*
    * Before we begin, let's see if malloc was successful.
    */
   if ( value != NULL )
   {
      /*
       * Loop until no matches are found.
       */
      for ( ;; )
      {
         /*
          * Try to find the search text.
          */
         const char *match = strstr(src, from);
         if ( match != NULL )
         {
            /*
             * Found search text at location 'match'. :)
             * Find out how many characters to copy up to the 'match'.
             */
            size_t count = match - src;
            /*
             * We are going to realloc, and for that we will need a
             * temporary pointer for safe usage.
             */
            char *temp;
            /*
             * Calculate the total size the string will be after the
             * replacement is performed.
             */
            size += tolen - fromlen;
            /*
             * Attempt to realloc memory for the new size.
             */
            temp = realloc(value, size);
            if ( temp == NULL )
            {
               /*
                * Attempt to realloc failed. Free the previously malloc'd
                * memory and return with our tail between our legs. :(
                */
               free(value);
               return NULL;
            }
            /*
             * The call to realloc was successful. :) But we'll want to
             * return 'value' eventually, so let's point it to the memory
             * that we are now working with. And let's not forget to point
             * to the right location in the destination as well.
             */
            dst = temp + (dst - value);
            value = temp;
            /*
             * Copy from the source to the point where we matched. Then
             * move the source pointer ahead by the amount we copied. And
             * move the destination pointer ahead by the same amount.
             */
            memmove(dst, src, count);
            src += count;
            dst += count;
            /*
             * Now copy in the replacement text 'to' at the position of
             * the match. Adjust the source pointer by the text we replaced.
             * Adjust the destination pointer by the amount of replacement
             * text.
             */
            memmove(dst, to, tolen);
            src += fromlen;
            dst += tolen;
         }
         else /* No match found. */
         {
            /*
             * Copy any remaining part of the string. This includes the null
             * termination character.
             */
            strcpy(dst, src);
            break;
         }
      }
   }
   return value;
}

void test(const char *source, const char *search, const char *repl)
{
   char *after;
   after = replace(source, search, repl);
   printf("\nsearch = \"%s\", repl = \"%s\"\n", search, repl);
   if ( after != NULL )
   {
      printf("after  = \"%s\"\n", after);
      free(after);
   }
}

int main(void)
{
   const char before[] = "the rain in Spain falls mainly on the plain";
   printf("before = \"%s\"\n", before);
   test(before, "the", "THEE");
   test(before, "the", "A");
   test(before, "cat", "DOG");
   test(before, "plain", "PLANE");
   test(before, "ain", "AINLY");
   return 0;
}

/* my output
before = "the rain in Spain falls mainly on the plain"

search = "the", repl = "THEE"
after  = "THEE rain in Spain falls mainly on THEE plain"

search = "the", repl = "A"
after  = "A rain in Spain falls mainly on A plain"

search = "cat", repl = "DOG"
after  = "the rain in Spain falls mainly on the plain"

search = "plain", repl = "PLANE"
after  = "the rain in Spain falls mainly on the PLANE"

search = "ain", repl = "AINLY"
after  = "the rAINLY in SpAINLY falls mAINLYly on the plAINLY"
*/
freerider 0 Newbie Poster

the function above have some bugs.... it fails for example with this:
replace("1.2.3.4.5.6.7.8.9",".","abcdsef");

this works every time:

char* _replace(char* source_str,char* search_str,char* replace_str)
{
char *ostr, *nstr = NULL, *pdest = "";
int length, nlen;
unsigned int nstr_allocated;
unsigned int ostr_allocated;


if(!source_str || !search_str || !replace_str){
printf("Not enough arguments\n");
return NULL;
}
ostr_allocated = sizeof(char) * (strlen(source_str)+1);
ostr = malloc( sizeof(char) * (strlen(source_str)+1));
if(!ostr){
printf("Insufficient memory available\n");
return NULL;
}
strcpy(ostr, source_str);


while(pdest)
{
pdest = strstr( ostr, search_str );
length = (int)(pdest - ostr);


if ( pdest != NULL )
{
ostr[length]='\0';
nlen = strlen(ostr)+strlen(replace_str)+strlen( strchr(ostr,0)+strlen(search_str) )+1;
if( !nstr || /* _msize( nstr ) */ nstr_allocated < sizeof(char) * nlen){
nstr_allocated = sizeof(char) * nlen;
nstr = malloc( sizeof(char) * nlen );
}
if(!nstr){
printf("Insufficient memory available\n");
return NULL;
}


strcpy(nstr, ostr);
strcat(nstr, replace_str);
strcat(nstr, strchr(ostr,0)+strlen(search_str));


if( /* _msize(ostr) */ ostr_allocated < sizeof(char)*strlen(nstr)+1 ){
ostr_allocated = sizeof(char)*strlen(nstr)+1;
ostr = malloc(sizeof(char)*strlen(nstr)+1 );
}
if(!ostr){
printf("Insufficient memory available\n");
return NULL;
}
strcpy(ostr, nstr);
}
}
if(nstr)
free(nstr);
return ostr;
}

I improved the one found here

so that is compatible with both windows-libs and gcc and g++

Dave Sinkula 2,398 long time no c Team Colleague

the function above have some bugs.... it fails for example with this:
replace("1.2.3.4.5.6.7.8.9",".","abcdsef");

Perhaps you could point out where?

int main(void)
{
   char *after = replace("1.2.3.4.5.6.7.8.9",".","abcdsef");
   if ( after != NULL )
   {
      printf("after  = \"%s\"\n", after);
      free(after);
   }
   return 0;
}

/* my output
after  = "1abcdsef2abcdsef3abcdsef4abcdsef5abcdsef6abcdsef7abcdsef8abcdsef9"
*/
mamashi 0 Newbie Poster

this is the simple one to replace a string . sorry put ur is too long and complicated :)

#include <stdafx.h>

#include<stdio.h>
#include<string.h>
int main
{
 char *foo = "???12?12???2"; 
 char buf[100];
 int l = strlen(foo);
int i;

 for(i=0;i<l;i++)
 {

        if(*foo=='?')
   buf[i] = ' ';
  else
   buf[i] = *foo;
        *foo++;

 }
 for(i=0;i<l;i++)
  printf("%c",buf[i]);
  printf("\n");
  getchar ();
}
ola_nordmann 0 Newbie Poster

Your code worked like a charm. (Debian, GCC 4.7)

Nonetheless, you shoud add cast to char* at lines 31 and 70 because malloc() and realloc() return argument are of type void*.

deceptikon 1,790 Code Sniper Team Colleague Featured Poster

Nonetheless, you shoud add cast to char* at lines 31 and 70 because malloc() and realloc() return argument are of type void*.

C supports implicit conversion both to and from void*. Best practice in C is not to cast, because the cast can hide a legitimate and common error of forgetting to include <stdlib.h>. However, if your personal style is to add the cast, it won't hurt anything as long as you're careful.

In C++ or C meant to be compatible with C++, you must include the cast, because C++ doesn't allow implicit conversion from void*.

mvmalderen commented: Thanks for including the reason ;) +13
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.