jacobay43
jacobay43

Reputation: 115

How to replace a part of a string with another substring

I need the string "on" to be replaced with "in", strstr() function returns a pointer to a string so i figured assigning the new value to that pointer would work but it didn't

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

int main(void) {
    char *m = "cat on couch";
    *strstr(m, "on") = "in";
    printf("%s\n", m);
}

Upvotes: 2

Views: 6264

Answers (3)

Jon Guiton
Jon Guiton

Reputation: 1415

This function performs a generic pattern replace for all instances of a substring with a replacement string. It allocates a buffer of the correct size for the result. Behaviour is well defined for the case of the empty substring corresponding to the javascript replace() semantics. Where possible memcpy is used in place of strcpy.

/*
 * strsub : substring and replace substring in strings.
 *
 * Function to replace a substring with a replacement string. Returns a
 * buffer of the correct size containing the input string with all instances
 * of the substring replaced by the replacement string.
 *
 * If the substring is empty the replace string is written before each character
 * and at the end of the string.
 *
 * Returns NULL on error after setting the error number.
 *
 */

char * strsub (char *input, char *substring, char *replace)
{
    int     number_of_matches = 0;
    size_t  substring_size = strlen(substring), replace_size = strlen(replace), buffer_size;
    char    *buffer, *bp, *ip;

/*
 * Count the number of non overlapping substring occurences in the input string. This
 * information is used to calculate the correct buffer size.
 */
    if (substring_size)
    {
        ip = strstr(input, substring);
        while (ip != NULL)
        {
            number_of_matches++;
            ip = strstr(ip+substring_size, substring);
        }
    }
    else
        number_of_matches = strlen (input) + 1;

/*
 * Allocate a buffer of the correct size for the output.
 */
    buffer_size = strlen(input) + number_of_matches*(replace_size - substring_size) + 1;

    if ((buffer = ((char *) malloc(buffer_size))) == NULL)
    {
        errno=ENOMEM;
        return NULL;
    }

/*
 * Rescan the string replacing each occurence of a match with the replacement string.
 * Take care to copy buffer content between matches or in the case of an empty find
 * string one character.
 */
    bp = buffer;
    ip = strstr(input, substring);
    while ((ip != NULL) && (*input != '\0'))
    {
        if (ip == input)
        {
            memcpy (bp, replace, replace_size+1);
            bp += replace_size;
            if (substring_size)
                input += substring_size;
            else
                *(bp++) = *(input++);
            ip = strstr(input, substring);
        }
        else 
            while (input != ip)
                *(bp++) = *(input++);

    }

/*
 * Write any remaining suffix to the buffer, or in the case of an empty find string
 * append the replacement pattern.
 */
    if (substring_size)
        strcpy (bp, input);
    else
        memcpy (bp, replace, replace_size+1);

    return buffer;
}

For testing purposes I include a main program that uses the replacement function.

    #define BUFSIZE 1024

    char * read_string (const char * prompt)
    {
        char *buf, *bp;

        if ((buf=(char *)malloc(BUFSIZE))==NULL)
        {
            error (0, ENOMEM, "Memory allocation failure in read_string");
            return NULL;
        }
        else
            bp=buf;

        printf ("%s\n> ", prompt);

        while ((*bp=getchar()) != '\n')bp++;
        *bp = '\0';

        return buf;
    }

    int main ()
    {
        char * input_string = read_string ("Please enter the input string");
        char * pattern_string = read_string ("Please enter the test string");
        char * replace_string = read_string ("Please enter the replacement string");

        char * output_string = strsub (input_string, pattern_string, replace_string);

        printf ("Result       :\n> %s\n", output_string);

        free (input_string);
        free (pattern_string);
        free (replace_string);
        free (output_string); 
        exit(0);
    }

Upvotes: 2

chqrlie
chqrlie

Reputation: 144715

Replacing a substring with another is easy if both substrings have the same length:

  • locate the position of the substring with strstr
  • if it is present, use memcpy to overwrite it with the new substring.
  • assigning the pointer with *strstr(m, "on") = "in"; is incorrect and should generate a compiler warning. You would avoid such mistakes with gcc -Wall -Werror.
  • note however that you cannot modify a string literal, you need to define an initialized array of char so you can modify it.

Here is a corrected version:

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

int main(void) {
    char m[] = "cat on couch";
    char *p = strstr(m, "on");
    if (p != NULL) {
        memcpy(p, "in", 2);
    }
    printf("%s\n", m);
    return 0;
}

If the replacement is shorter, the code is a little more complicated:

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

int main(void) {
    char m[] = "cat is out roaming";
    char *p = strstr(m, "out");
    if (p != NULL) {
        memcpy(p, "in", 2);
        memmove(p + 2, p + 3, strlen(p + 3) + 1);
    }
    printf("%s\n", m);
    return 0;
}

In the generic case, it is even more complicated and the array must be large enough to accommodate for the length difference:

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

int main(void) {
    char m[30] = "cat is inside the barn";
    char *p = strstr(m, "inside");
    if (p != NULL) {
        memmove(p + 7, p + 6, strlen(p + 6) + 1);
        memcpy(p, "outside", 7);
    }
    printf("%s\n", m);
    return 0;
}

Here is a generic function that handles all cases:

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

char *strreplace(char *s, const char *s1, const char *s2) {
    char *p = strstr(s, s1);
    if (p != NULL) {
        size_t len1 = strlen(s1);
        size_t len2 = strlen(s2);
        if (len1 != len2)
            memmove(p + len2, p + len1, strlen(p + len1) + 1);
        memcpy(p, s2, len2);
    }
    return s;
}

int main(void) {
    char m[30] = "cat is inside the barn";

    printf("%s\n", m);
    printf("%s\n", strreplace(m, "inside", "in"));
    printf("%s\n", strreplace(m, "in", "on"));
    printf("%s\n", strreplace(m, "on", "outside"));
    return 0;
}

Upvotes: 5

Govind Parmar
Govind Parmar

Reputation: 21542

There are a few problems with this approach. First, off, m is pointing to read-only memory, so attempting to overwrite the memory there it is undefined behavior.

Second, the line: strstr(m, "on") = "in" is not going to change the pointed-to string, but instead reassign the pointer.

Solution:

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

int main(void)
{
    char m[] = "cat on couch";
    memcpy(strstr(m, "on"), "in", 2);
    printf("%s\n", m);
}

Note that if you had just used plain strcpy it would null-terminate after "cat in", so memcpy is necessary here. strncpy will also work, but you should read this discussion before using it.

It should also be known that if you are dealing with strings that are not hard-coded constants in your program, you should always check the return value of strstr, strchr, and related functions for NULL.

Upvotes: 2

Related Questions