替换整个单词而不是C中字符串的子字符串

时间:2019-02-27 20:36:01

标签: c string replace word

我正在尝试替换C字符数组中的整个单词,并跳过子字符串。我进行了研究,最终得到了非常艰难的解决方案,而我认为如果有人可以帮助我,我会更好。 假设我有以下字符串:

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

我想将树替换为数字12:

"apple 12 house"

我知道单词之间用空格分隔,所以我的想法是:

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 :(得分:2)

在这种情况下,我会避免使用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';
}

(请注意,我对您的函数签名进行了调整,以使用更惯用的指针表示法而不是char数组,并且在下面的flu注释中,我将搜索标记并替换为“ const”,这是函数宣传它的一种方式不会修改这些字符串。)

答案 1 :(得分:0)

要做您想做的事情变得有些复杂,因为您需要处理以下情况:

  1. 替换比原始短-因此,您需要移动其余行以跟随替换文本,以免留空;
  2. 替换长度与原始长度相同-很小的情况,只是用替换覆盖原始内容;最后
  3. 替换比原始长-您必须验证原始字符串,并且替换长度差仍将适合原始字符串的存储,您必须将行尾复制到临时缓冲区,然后再进行替换,然后将临时缓冲区中的其余行添加到末尾。

strtok在这里有一些缺点,因为它在标记化过程中对原始字符串进行了更改。 (您可以只复制一份,但是如果要就地替换,则需要进一步检查)。在strstrstrcspn的组合中查找原始字符串中的特定搜索字符串时,可以更有效地对原始字符串进行操作。

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

解决此问题的方法有很多种,因此没有一种方法是正确的方法。关键是使其易于理解和合理有效。仔细研究一下,如果您还有其他问题,请告诉我。