Home > Net >  Replace a string in C without string.h library
Replace a string in C without string.h library

Time:02-16

I want to make a function which will replace two strings without using <string.h> library. In order to achieve that, I used 6 manually written functions which are together achieving this task. I have a small problem with this code, this doesn't check if string which will be replaced is word which is completely equal to replacing string.

For example:

char s[] = "Why is ostring in C so hard",
     change_what[] = "string",
     into_what[] = "pointers";

OUTPUT should be:

"Why is ostring in C so hard"

Because "ostring" is not completely equal to "string".

My output is:

"Why are opointers in C so hard"

Code:

#include <stdio.h>

int compare(char *x, char *y)
{
    while (*x != '\0' || *y != '\0')
    {
        if (*x == *y)
        {
            x  ;
            y  ;
        }
        // if they are not equal
        else if ((*x == '\0' && *y != '\0') || (*x != '\0' && *y == '\0') ||
                 *x != *y)
        {
            return 0;
        }
    }
    return 1;
}

int lenght(char *a)
{
    char *b;
    for (b = a; *a; a  )
        ;
    return a - b;
}

char *substring(char *main_string, char *substring) {
    while (*main_string != '\0') {
        char *p = main_string;
        char *q = substring;
        while (*p   == *q  ) {
            if (*p == ' ' || *p == '\0')
               if (*q == '\0') {
                   return main_string;
            }
        }
        main_string  ;
    }
    return NULL;
}

void replace_string_add(char *s, char *change_what, char *into_what,
                        int shift)
{
    char *i_pointer = into_what;
    char *c_pointer = change_what;
    char *position = substring(s, change_what);
    while (position != NULL)
    {
        char *end = position;
        while (*end != '\0')
        {
            end  ;
        }
        while (end > position)
        {
            *(end   shift) = *end;
            end--;
        }
        while (*into_what != '\0')
        {
            *position   = *into_what  ;
        }
        position = substring(s, change_what);
        into_what = i_pointer;
        change_what = c_pointer;
    }
}

void replace_string_remove(char *s, char *change_what, char *into_what,
                           int shift)
{
    char *i_pointer = into_what;
    char *c_pointer = change_what;
    char *position = substring(s, change_what);
    while (position != NULL)
    {
        char *temp = position;
        while (*(temp   shift) != '\0')
        {
            *temp = *(temp   shift);
            temp  ;
        }
        *temp = '\0';
        while (*into_what != '\0')
        {
            *position   = *into_what  ;
        }
        position = substring(s, change_what);
        into_what = i_pointer;
        change_what = c_pointer;
    }
}

void replace_string(char *s, char *change_what, char *into_what)
{
    int shift = lenght(into_what) - lenght(change_what);
    if (compare(change_what, into_what) == 0)
    {
        if (shift >= 0)
        {
            replace_string_add(s, change_what, into_what, shift);
        }
        else
        {
            replace_string_remove(s, change_what, into_what, -shift);
        }
    }
}

int main()
{
    char s[] = "Why is ostring in C so hard",
         change_what[] = "string",
         into_what[] = "pointers";
    replace_string(s, change_what, into_what);
    printf("\"%s\"", s);
    return 0;
}

If string is "Why is strings in C so hard" program would work correct because it checks if last characters are ' ' or '\0'.

"Why is ostring in C so hard" wouldn't work, because it doesn't check first character. Could you help me modify this code to check also first character?

  • Note: auxiliary strings and dynamic allocation are not allowed

CodePudding user response:

Your program has multiple issues:

  • there is a lot of redundant code in the compare function. You can simplify it as:

    int compare(const char *x, const char *y) {
        while (*x != '\0' || *y != '\0') {
            if (*x == *y) {
                x  ;
                y  ;
            } else {
                return 0;
            }
        }
        return 1;
    }
    

    or even further:

    int compare(const char *x, const char *y) {
        while (*x   == *y  ) {
            if (x[-1] == '\0')
                return 1;
        }
        return 0;
    }
    
  • the lenght function should be named length

  • substring checks for a space after the end of the substring, but does not check for a space before the start. It also has undefined behavior if the substring matches the end of the main_string because characters beyond the null terminator will be accessed. Here is a modified version:

    char *substring(char *main_string, const char *substring) {
        char *p = main_string;
        char last = ' ';
        while (*p != '\0') {
            if (last == ' ') {
                size_t i = 0;
                while (substring[i] != '\0' && p[i] == substring[i]) {
                    i  ;
                }
                if (substring[i] == '\0' && (p[i] == ' ' || p[i] == '\0')) {
                    return p;
                }
            }
            last = *p  ;
        }
        return NULL;
    }
    
  • in replace_string_add and replace_string_remove, c_pointer is useless and it would be less confusing to use i_pointer to copy the replacement than modify into_what and restore it.

Note also that the main_string argument must have enough space for the replacements, which would not be the case in the example if the change_what string was "ostring".

Here is a modified version:

#include <stdio.h>

int compare(const char *x, const char *y) {
    while (*x   == *y  ) {
        if (x[-1] == '\0')
            return 1;
    }
    return 0;
}

int length(const char *a) {
    const char *b;
    for (b = a; *a; a  )
        continue;
    return a - b;
}

char *substring(char *main_string, const char *substring, int len) {
    char *p = main_string;
    char last = ' ';
    while (*p != '\0') {
        if (last == ' ') {
            int i = 0;
            while (i < len && p[i] == substring[i]) {
                i  ;
            }
            if (i == len && (p[i] == ' ' || p[i] == '\0')) {
                return p;
            }
        }
        last = *p  ;
    }
    return NULL;
}

char *replace_string(char *s, const char *change_what, const char *into_what) {
    int what_len = length(change_what);
    int into_len = length(into_what);
    int shift = into_len - what_len;
    int i;
    char *pos = s;

    if (shift == 0 && compare(change_what, into_what))
        return s;

    while (*pos && (pos = substring(pos, change_what, what_len)) != NULL) {
        if (shift > 0) {
            for (i = length(pos); i >= what_len; i--) {
                pos[i   shift] = pos[i];
            }
        } else
        if (shift < 0) {
            for (i = into_len; ((pos[i] = pos[i - shift]) != '\0'; i  ) {
                continue;
            }
        }
        for (i = 0; i < into_len; i  ) {
            *pos   = into_what[i];
        }
        if (*pos == ' ') {
            pos  ;
        }
    }
    return s;
}

int main() {
    char s[100] = "Why is ostring in C so hard";
    printf("\"%s\"\n", replace_string(s, "string", "pointer"));
    printf("\"%s\"\n", replace_string(s, "ostring", "pointers"));
    printf("\"%s\"\n", replace_string(s, "is", "are"));
    printf("\"%s\"\n", replace_string(s, "hard", "cool"));
    printf("\"%s\"\n", replace_string(s, "pointers", "strings"));
    printf("\"%s\"\n", replace_string(s, "in C", ""));
    printf("\"%s\"\n", replace_string(s, "", "in C   not"));
    printf("\"%s\"\n", replace_string(s, "", ""));
    return 0;
}

Output:

"Why is ostring in C so hard"
"Why is pointers in C so hard"
"Why are pointers in C so hard"
"Why are pointers in C so cool"
"Why are strings in C so cool"
"Why are strings  so cool"
"Why are strings in C   not so cool"
"Why are strings in C   not so cool"

CodePudding user response:

ok clear, I think it's easier to use a string tokenizer for this. The following code first copies s into s2 and then puts a '\0' at every space and keeps track of how many words there are. the first for loop. Then clear out s and loop through s2 stopping at every '\0' sentinel and check if every word equals your replace word. If it matches copy back the replace word otherwise the original word.

#include <stdio.h>

static void cat(char *d, char *s)
{
    char *p;
    for (p = d; *p; p  );
    
    for (char *p2 = s; *p2;)
      *p   = *p2  ;
    *p = 0;
}

static int cmp(char *s1, char *s2)
{
    char *p1 = s1, *p2 = s2;
    
    while (*p1 && *p2 && *p1 == *p2)
    {
        p1  ;
        p2  ;
    }

    if (*p1 == 0 && *p2 == 0) return 0;
    
    return -1;
}

int main()
{
    char s[100]="Why are strings in C so hard",change_what[]="strings", into_what[]="pointers";
    
    char s2[100];
    
    for (char *p = s, *p2 = s2; *p2=*p; p  , p2  );
    
    int countwords = 0;
    
    for (char *p = s2; *p; p  )
       if (*p == ' ') { *p = '\0'; countwords  ; }

    int current = 0;
    
    char *s3 = s2;
    *s = '\0';
    
    while (current <= countwords)
    {
        char *p = s3;
        for (; *p; p  );
        
        if (cmp(s3, change_what) == 0)
            cat(s, into_what);
        else
            cat(s, s3);
            
        cat(s, " ");
            
        s3 = p   1;

        current  ;
    }
    
    printf("%s\n", s);
    
    return 0;
}
  • Related