Замена целого слова, а не подстрок в строке в C - PullRequest
0 голосов
/ 27 февраля 2019

Я пытаюсь заменить целое слово в массиве символов C и пропустить подстроки.Я провел исследование, и в результате я получил действительно жесткие решения, хотя думаю, что у меня есть идея, может ли кто-нибудь помочь мне.Допустим, у меня есть строка:

char sentence[100]= "apple tree house";

И я хотел бы заменить дерево на число 12:

"apple 12 house"

Я знаю, что слова разделены пробелом, поэтому моя идеяto:

1. Взять строку с пробелом
2. В цикле while выполняется проверка с помощью библиотечной функции STRCMP, совпадает ли строка с токеном и подлежит ли она замене.

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

void wordreplace(char string[], char search[], char replace[]) {

  // Tokenize
  char * token = strtok(string, " ");

  while (token != NULL) {
    if (strcmp(search, token) == 0) {
      REPLACE SEARCH STRING WITH REPLACE STRING
    }
    token = strtok(NULL, " ");
  }
  printf("Sentence : %s", string);

}

Какие-либо предложения, что я могу использовать?Я думаю, это может быть очень просто, но я новичок, которого очень ценят:)
[РЕДАКТИРОВАТЬ]: пробелы являются единственными разделителями, и обычно строка, которая должна быть заменена, не длиннее оригинальной.

Ответы [ 2 ]

0 голосов
/ 28 февраля 2019

Делать то, что вы хотите, становится немного сложнее, потому что вам нужно работать со сценариями, в которых:

  1. замена короче оригинала - так что вам понадобитсяпереместить остаток строки, следуя тексту замены, чтобы не оставлять пустого места;
  2. замена имеет ту же длину , что и оригинал - простой случай, просто перезаписать оригинал с заменой;и, наконец,
  3. замена длиннее, чем оригинал - где вы должны проверить исходную строку, плюс разница длины замены все равно будет помещаться в хранилище для исходной строки, вы должны скопировать конецстрока во временный буфер перед выполнением замены, а затем добавьте в конец оставшуюся часть строки во временном буфере.

strtok некоторые недостатки здесь из-за внесения изменений в исходныйстрока во время процесса токенизации.(вы можете просто сделать копию, но если вы хотите заменить на месте, вам нужно посмотреть дальше).Комбинация strstr и strcspn позволяет вам более эффективно работать с исходной строкой при поиске конкретной строки поиска в оригинале.

strcspn может использоваться как strtok с набором разделителей для предоставления длины текущего найденного токена (чтобы убедиться, что strstr не соответствует вашему поисковому запросу как менеевключенная подстрока более длинного слова, например tree в trees). Затем становится простым циклом с strstr и проверкой длины токена с помощью strcspn, а затем просто применением одного изтри приведенных выше случая.

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

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

#define MAXLIN 100

void wordreplace (char *str, const char *srch,
                    const char *repl, const char *delim)
{
    char *p = str;                      /* pointer to str */
    size_t  lenword,                    /* length of word found */
            lenstr = strlen (str),      /* length of total string */
            lensrch = strlen (srch),    /* length of search word */
            lenrepl = strlen (repl);    /* length of replace word */

    while ((p = strstr (p, srch))) {    /* srch exist in rest of string? */
            lenword = strcspn (p, delim);   /* get length of word found */
        if (lenword == lensrch) {       /* word len match search len */
            if (lenrepl == lensrch)     /* if replace is same len */
                memcpy (p, repl, lenrepl);  /* just copy over */
            else if (lenrepl > lensrch) {   /* if replace is longer */
                /* check that additional lenght will fit in str */
                if (lenstr + lenrepl - lensrch > MAXLIN - 1) {
                    fputs ("error: replaced length would exeed size.\n",
                            stderr);
                    return;
                }
                if (!p[lenword]) {          /* if no following char */
                    memcpy (p, repl, lenrepl);  /* just copy replace */
                    p[lenrepl] = 0;             /* and nul-terminate */
                }
                else {  /* store rest of line in buffer, replace, add end */
                    char endbuf[MAXLIN];    /* temp buffer for end */
                    size_t lenend = strlen (p + lensrch);   /* end length */
                    memcpy (endbuf, p + lensrch, lenend + 1); /* copy end */
                    memcpy (p, repl, lenrepl);  /* make replacement */
                    memcpy (p + lenrepl, endbuf, lenend); /* add end after */
                }
            }
            else {  /* otherwise replace is shorter than search */
                size_t lenend = strlen (p + lenword);   /* get end length */
                memcpy (p, repl, lenrepl);              /* copy replace */
                /* move end to after replace */
                memmove (p + lenrepl, p + lenword, lenend + 1);
            }
        }
    }
}

int main (int argc, char **argv) {

    char str[MAXLIN] = "apple tree house in the elm tree";
    const char  *search = argc > 1 ? argv[1] : "tree",
                *replace = argc > 2 ? argv[2] : "12",
                *delim = " \t\n";

    wordreplace (str, search, replace, delim);

    printf ("str: %s\n", str);
}

Пример использования / Вывод

Ваша замена "tree" на "12" пример в "apple tree house in the elm tree":

$ ./bin/wordrepl_strstr_strcspn
str: apple 12 house in the elm 12

Простая замена такой же длины "tree" на "core", например

$ ./bin/wordrepl_strstr_strcspn tree core
str: apple core house in the elm core

«больше, чем», заменить "tree" на "bobbing":

$ ./bin/wordrepl_strstr_strcspn tree bobbing
str: apple bobbing house in the elm bobbing

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

0 голосов
/ 28 февраля 2019

Я бы избегал strtok в этом случае (потому что это изменит строку как побочный эффект ее токенизации), и подхожу к этому, рассматривая строку по существу посимвольно и поддерживая «чтение» и «написать "указатель.Поскольку выходные данные никогда не могут быть длиннее входных, индекс записи никогда не опережает прочитанный, и вы можете выполнить «обратную запись» и внести изменения в одной и той же строке.

Чтобы визуализировать это,Я считаю полезным записывать ввод в прямоугольники и рисовать стрелки для текущих индексов чтения и записи и отслеживать процесс, чтобы вы могли убедиться, что у вас есть система, которая будет делать то, что вы хотите, и что ваши циклы и индексы всеработать так, как вы ожидаете.

Вот одна реализация, которая соответствует тому, как мой собственный разум стремится приблизиться к алгоритму такого рода.Он идет по строке и смотрит вперед, чтобы попытаться найти совпадения с текущим символом.Если он находит совпадение, он копирует замену в текущую точку и соответственно увеличивает оба индекса.

void wordreplace(char * string, const char * search, const char * replace) {

    // This is required to be true since we're going to do the replace
    // in-place:
    assert(strlen(replace) <= strlen(search)); 

    // Get ourselves set up
    int r = 0, w = 0; 
    int str_len = strlen(string);
    int search_len = strlen(search);
    int replace_len = strlen(replace);

    // Walk through the input character by character.
    while (r < str_len) {

        // Is this character the start of a matching token? It is
        // if we see the search string followed by a space or end of
        // string. 
        if (strncmp(&string[r], search, search_len) == 0 &&
            (string[r+search_len] == ' ' || string[r+search_len] == '\0')) { 

            // We matched the search token. Copy the replace token.
            memcpy(&string[w], replace, replace_len);

            // Update our indexes.
            w += replace_len;
            r += search_len;
        } else {
            // Otherwise just copy this character.
            string[w++] = string[r++];
        }
    }

    // Be sure to terminate the final version of the string.
    string[w] = '\0';
}

(Обратите внимание, что я настроил сигнатуру вашей функции, чтобы использовать более идиоматическую нотацию указателя, а не массивы символов,и согласно комментарию гриппа ниже, я пометил токены поиска и замены как «const», который является способом объявления функции, что он не будет изменять эти строки.)

...