функция replace substring возвращает неверный ответ - PullRequest
2 голосов
/ 20 июня 2020

У меня есть программа, заменяющая подстроку в строке. Идея состоит в том, чтобы найти string_to_be_replaced в original_string, затем realloc new_string и связать его со строкой replace_by. Он работает в некоторых случаях, но в некоторых случаях, как показано ниже, он возвращает неправильный ответ:

Вход:

abc def ghi //orginal string

(a blank space) //string to be replaced

1234 //replace by

Выход:

abc1234defT123ghi

Ожидаемый результат:

abc1234def1234ghi

Когда я отлаживаю его, я увидел, что в new_string был введен неправильный символ после того, как была сделана первая замена .

Подскажите пожалуйста, почему так происходит и как это исправить. Любая помощь будет принята с благодарностью.

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

void fgetsWithoutNewline(char *s, size_t maxCount, FILE *fp);
bool sameString(char *original_tring, char *string_to_be_searched, size_t start);
char *replaceString(char *original_tring, char *string_to_be_replaced, char *replace_by);

int main()
{
    char *original_string = malloc(100);
    fgetsWithoutNewline(original_string, 99, stdin);

    char *string_to_be_replaced = malloc(100);
    fgetsWithoutNewline(string_to_be_replaced, 99, stdin);

    char *replace_by = malloc(100);
    fgetsWithoutNewline(replace_by, 99, stdin);

    char *s = replaceString(original_string, string_to_be_replaced, replace_by);
    printf("%s", s);

    free(original_string);
    free(string_to_be_replaced);
    free(replace_by);
    return 0;
}

void fgetsWithoutNewline(char *s, size_t maxCount, FILE *fp)
{
    if (fgets(s, maxCount, fp))
    {
        s[strcspn(s, "\n")] = '\0';
    }
    return;
}

char *replaceString(char *original_tring, char *string_to_be_replaced, char *replace_by)
{
    if (!original_tring || !string_to_be_replaced || !replace_by)
    {
        return NULL;
    }
    char *new_string = malloc(strlen(original_tring));

    for (size_t i = 0, j = 0; i < strlen(original_tring); i++, j++)
    {
        if (sameString(original_tring, string_to_be_replaced, i))
        {
            new_string = realloc(new_string, strlen(new_string) + strlen(replace_by) - strlen(string_to_be_replaced));
            strcat(new_string, replace_by);
            i += strlen(string_to_be_replaced) - 1;  // i and j use to track the last character of original string and new string
            j += strlen(replace_by) - 1;
        }
        else
        {
            new_string[j] = original_tring[i];
        }
    }
    return new_string;
}

bool sameString(char *original_tring, char *string_to_be_searched, size_t start)
{
    if (strlen(string_to_be_searched) + start > strlen(original_tring))
    {
        return false;
    }
    size_t end = strlen(string_to_be_searched) + start;
    for (size_t i = start, j = 0; i < end; i++, j++)
    {
        if (original_tring[i] != string_to_be_searched[j])
        {
            return false;
        }
    }
    return true;
}

Ответы [ 3 ]

2 голосов
/ 20 июня 2020

Проблема в том, что вы не добавляете требуемый символ-ограничитель nul в буфер new_string (в функции replace_string). В системах, которые случайно заполняют любые новые данные, «созданные» malloc или realloc нулями, это будет трудно найти; но это не стандартное поведение, и вы должны явно убедиться, что признак nul всегда присутствует.

Для начального распределения new_string, это могло быть выполнено с помощью функции calloc; однако, поскольку вы (вероятно) вызываете realloc, чтобы увеличить размер буфера хотя бы один раз, вам нужно будет убедиться, что вновь добавленная память также имеет nul, поэтому вам действительно нужно добавить этот терминатор при каждый прогон for l oop:

char* replaceString(char* original_tring, char* string_to_be_replaced, char* replace_by)
{
    if (!original_tring || !string_to_be_replaced || !replace_by) {
        return NULL;
    }
    char* new_string = malloc(strlen(original_tring));
    for (size_t i = 0, j = 0; i < strlen(original_tring); i++, j++) {
        if (sameString(original_tring, string_to_be_replaced, i)) {
            new_string = realloc(new_string, strlen(new_string) + strlen(replace_by) - strlen(string_to_be_replaced));
            strcat(new_string, replace_by);
            i += strlen(string_to_be_replaced) - 1;  // i and j use to track the last character of original string and new string
            j += strlen(replace_by) - 1;
        }
        else {
            new_string[j] = original_tring[i];
        }
        new_string[j + 1] = '\0';/// Add new nul-terminator!
    }
    return new_string;
}
2 голосов
/ 20 июня 2020

Что ж, симптомы очевидны, строка не завершается нулем.

Попробуйте следующее:

Live demo

char *replaceString(char *original_tring, char *string_to_be_replaced, const char *replace_by)
{
    if (!original_tring || !string_to_be_replaced || !replace_by)
    {
        return NULL;
    }
    char *new_string = malloc(strlen(original_tring));

    for (size_t i = 0, j = 0; i < strlen(original_tring); i++, j++)
    {
        if (sameString(original_tring, string_to_be_replaced, i))
        {
            new_string = realloc(new_string, strlen(new_string) + strlen(replace_by) - strlen(string_to_be_replaced));
            strcat(new_string, replace_by);
            i += strlen(string_to_be_replaced) - 1;
            j += strlen(replace_by) - 1;
        }
        else
        {
            new_string[j] = original_tring[i];
        }
        new_string[j + 1] = '\0'; //here
    }  
    return new_string;
}
1 голос
/ 20 июня 2020

Ваша функция (абстрагирование от отсутствующего терминатора nul) очень неэффективна.

Ниже приведены функции

Эта функция использует ту же строку для хранения результата:

char *stringreplace(char *haystack, const char *needle, const char *replace)
{
    size_t needleLen = strlen(needle);
    size_t replaceLen = strlen(replace);
    char *wrk = haystack;
    char *end = haystack + strlen(haystack);



    while((wrk = strstr(wrk, needle)))
    {
        memmove(wrk + replaceLen, wrk + needleLen, end - wrk + 1);
        memcpy(wrk, replace, replaceLen);
        wrk += replaceLen;
        end += replaceLen - needleLen;
    }
    return haystack;
}

Здесь используется динамическое c распределение. Обратите внимание, что во многих системах malloc и особенно realloc являются очень дорогостоящими операциями, и часто стоит дважды пройти по строке и вызвать только один mallo c, зная размер результирующей строки.

char *dupstringreplace(const char *haystack, const char *needle, const char *replace)
{
    char *newhaystack = NULL;
    const char *wrk = haystack;
    char *newwrk;
    ssize_t occurences = 0;
    size_t needleLen = strlen(needle);
    size_t replaceLen = strlen(replace);

    while((wrk = strstr(wrk, needle)))
    {
        occurences++;
        wrk += needleLen;
    }
    newhaystack = malloc(strlen(haystack) + occurences * ((ssize_t)replaceLen - (ssize_t)needleLen) + 1);
    wrk = haystack;
    newwrk = newhaystack;
    while((wrk = strstr(wrk, needle)))
    {
        memcpy(newwrk, haystack, wrk - haystack);
        newwrk += wrk - haystack;
        memcpy(newwrk, replace, replaceLen);
        newwrk += replaceLen;
        wrk += needleLen;
        haystack = wrk;
    }
    strcpy(newwrk, haystack);
    return newhaystack;
}

крестовина

...