【问题标题】:How to replace a part of a string with another substring如何用另一个子字符串替换字符串的一部分
【发布时间】:2019-09-26 08:33:49
【问题描述】:

我需要将字符串“on”替换为“in”,strstr() 函数返回一个指向字符串的指针,所以我认为将新值分配给该指针会起作用,但它没有

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

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

【问题讨论】:

    标签: c string.h


    【解决方案1】:

    此函数使用替换字符串对子字符串的所有实例执行通用模式替换。它为结果分配正确大小的缓冲区。对于与 javascript replace() 语义对应的空子字符串的情况,行为已得到很好的定义。尽可能使用 memcpy 代替 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;
    }
    

    出于测试目的,我包含了一个使用替换功能的主程序。

        #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);
        }
    

    【讨论】:

    • 所有对strcat的调用都应该是对strcpy的调用,或者更好的是memcpy :)
    • 改进了在可能的情况下使用 memcpy;改进的 cmets。
    • 这里是进一步简化的想法:for (ip = input; (ip = strstr(ip, substring)) != NULL; ip += substring_size) { number_of_matches++; },第一个memcpy (bp, replace, replace_size+1);不需要复制空字节。为案例substring_size == 0 单独循环可能是有意义的,这会导致代码更简单。
    【解决方案2】:

    如果两个子字符串的长度相同,则用另一个子字符串替换一个子字符串很容易:

    • 用strstr定位子串的位置
    • 如果存在,请使用memcpy 用新的子字符串覆盖它。
    • 用*strstr(m, "on") = "in"; 分配指针不正确,应生成编译器警告。使用gcc -Wall -Werror 可以避免此类错误。
    • 但是请注意,您不能修改字符串文字,您需要定义一个 char 的初始化数组,以便您可以修改它。

    这是一个更正的版本:

    #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;
    }
    

    如果替换更短,代码会稍微复杂一点:

    #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;
    }
    

    在泛型情况下,就更复杂了,数组必须足够大以适应长度差异:

    #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;
    }
    

    这是一个处理所有情况的通用函数:

    #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;
    }
    

    【讨论】:

      【解决方案3】:

      这种方法存在一些问题。首先,关闭,m 指向只读内存,因此尝试覆盖那里的内存是未定义的行为。

      第二行:strstr(m, "on") = "in" 不会更改指向的字符串,而是重新分配指针。

      解决方案:

      #include <stdio.h>
      #include <string.h>
      
      int main(void)
      {
          char m[] = "cat on couch";
          memcpy(strstr(m, "on"), "in", 2);
          printf("%s\n", m);
      }
      

      请注意,如果您刚刚使用普通的strcpy,它将在"cat in" 之后空终止,因此这里需要memcpy。 strncpy 也可以,但您应该在使用之前阅读this discussion。

      还应该知道,如果您在程序中处理不是硬编码常量的字符串,则应始终检查strstr、strchr 的返回值以及NULL 的相关函数。

      【讨论】:

        猜你喜欢
        • 2022-12-28
        • 1970-01-01
        • 2011-03-25
        • 2011-08-10
        • 2014-06-03
        • 1970-01-01
        相关资源
        最近更新 更多