什么功能从C中的字符串替换子字符串?


94

给定一个(char *)字符串,我想查找所有出现的子字符串并将其替换为备用字符串。我没有在中看到任何简单的功能来实现此目的<string.h>


4
我怀疑您可以以可变的方式进行此操作
user44511

Answers:


87

优化器应消除大多数局部变量。tmp指针可以确保strcpy不必遍历字符串即可找到null。tmp指向每次调用后结果的结尾。(请参阅画家的算法Shlemiel,了解为什么strcpy会令人讨厌。)

// You must free the result if result is non-NULL.
char *str_replace(char *orig, char *rep, char *with) {
    char *result; // the return string
    char *ins;    // the next insert point
    char *tmp;    // varies
    int len_rep;  // length of rep (the string to remove)
    int len_with; // length of with (the string to replace rep with)
    int len_front; // distance between rep and end of last rep
    int count;    // number of replacements

    // sanity checks and initialization
    if (!orig || !rep)
        return NULL;
    len_rep = strlen(rep);
    if (len_rep == 0)
        return NULL; // empty rep causes infinite loop during count
    if (!with)
        with = "";
    len_with = strlen(with);

    // count the number of replacements needed
    ins = orig;
    for (count = 0; tmp = strstr(ins, rep); ++count) {
        ins = tmp + len_rep;
    }

    tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);

    if (!result)
        return NULL;

    // first time through the loop, all the variable are set correctly
    // from here on,
    //    tmp points to the end of the result string
    //    ins points to the next occurrence of rep in orig
    //    orig points to the remainder of orig after "end of rep"
    while (count--) {
        ins = strstr(orig, rep);
        len_front = ins - orig;
        tmp = strncpy(tmp, orig, len_front) + len_front;
        tmp = strcpy(tmp, with) + len_with;
        orig += len_front + len_rep; // move to next "end of rep"
    }
    strcpy(tmp, orig);
    return result;
}

@jmucchiello:使用size_t代替以int获得任意的对象/字符串大小和索引。另外,最后的目的是什么strcpy(tmp, orig);?好像错了
Alexey Frunze

@Alex,最后一个strcpy(tmp,orig)将字符串的最后一部分复制到目标。例如:循环末尾的replace(“ abab”,“ a”,“ c”),结果包含“ cbc”,并且orig指向“ abab”中的最后一个“ b”。最后一个strcpy附加“ b”,因此返回的字符串为“ cbcb”。如果没有要复制的内容,则orig应该指向输入字符串的ASCIIZ。
jmucchiello 2011年

简化:您可以将第一个for循环替换为for (count = 1; ins = strstr(ins + rep_len, rep); ++count) {},然后tmp仅用于编写。
猖ion

1
char * done = replace(“ abcdefghijkl”,“ bc”,“ yz”); 做东西(); 免费(完成);
jmucchiello

2
请注意,如果没有要替换的事件,此函数将返回NULL(如果(!(ins = strstr(orig,rep)))返回NULL;)。您不能只使用输出,还需要检查输出是否为NULL,是否使用原始字符串(不要仅将指针复制到结果字符串,因为free(result)会释放原始字符串)。如果没有替换的内容,只是将输入字符串复制到输出字符串中,则用法更为简单。
Adversus

18

这在标准C库中未提供,因为仅给定一个char *,如果替换字符串比被替换的字符串长,您将无法增加分配给该字符串的内存。

您可以更容易地使用std :: string进行此操作,但是即使在那儿,也没有哪个函数可以为您完成此操作。


12
这个问题是关于C,而不是C ++。
Geremia

1 / strlen(char *)+ 1不一定等于存储大小。2 /有很多N版本的字符串函数接收和附加的缓冲区大小参数,因此没有理由不能存在snreplace()。3 /可能有就地替换功能,而不是就地替换功能。4 /您认为sprintf如何工作?它有一个char *参数,不需要增加它的内存分配,因此没有理由替换也不能工作...(尽管C的“字符串”设计很差,并且缓冲区大小始终应该通过指针=> snprintf)
史蒂文·斯帕克


11

您可以使用strstr来找到子字符串,并使用strncpy来部分复制到新缓冲区中来构建自己的替换函数。

除非您想要replace_with的长度与您想要的长度相同replace,否则最好使用新的缓冲区将新字符串复制到其中。


9

由于C中的字符串不能动态增长,因此替换通常不会起作用。因此,您需要为新字符串分配空间,该字符串具有足够的替代空间,然后将原始副本和替换部分复制到新字符串中。要复制零件,可以使用strncpy


缓冲区大小可以大于strlen,替换字符串可以小于替换字符串...因此,您不需要分配内存来执行替换。(此外,在微控制器上,您可能没有无限的内存,可能需要执行替换操作。将所有内容复制到新的缓冲区可能并不是适合所有人的解决方案……)
Steven Spark

8

这是一些示例代码。

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

char * replace(
    char const * const original, 
    char const * const pattern, 
    char const * const replacement
) {
  size_t const replen = strlen(replacement);
  size_t const patlen = strlen(pattern);
  size_t const orilen = strlen(original);

  size_t patcnt = 0;
  const char * oriptr;
  const char * patloc;

  // find how many times the pattern occurs in the original string
  for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen)
  {
    patcnt++;
  }

  {
    // allocate memory for the new string
    size_t const retlen = orilen + patcnt * (replen - patlen);
    char * const returned = (char *) malloc( sizeof(char) * (retlen + 1) );

    if (returned != NULL)
    {
      // copy the original string, 
      // replacing all the instances of the pattern
      char * retptr = returned;
      for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen)
      {
        size_t const skplen = patloc - oriptr;
        // copy the section until the occurence of the pattern
        strncpy(retptr, oriptr, skplen);
        retptr += skplen;
        // copy the replacement 
        strncpy(retptr, replacement, replen);
        retptr += replen;
      }
      // copy the rest of the string.
      strcpy(retptr, oriptr);
    }
    return returned;
  }
}

#include <stdio.h>
int main(int argc, char * argv[])
{
  if (argc != 4)
  {
    fprintf(stderr,"usage: %s <original text> <pattern> <replacement>\n", argv[0]);
    exit(-1);
  }
  else
  {
    char * const newstr = replace(argv[1], argv[2], argv[3]);
    if (newstr)
    {
      printf("%s\n", newstr);
      free(newstr);
    }
    else
    {
      fprintf(stderr,"allocation error\n");
      exit(-2);
    }
  }
  return 0;
}

它可以工作,但是有点问题,但是还是要谢谢!:D这是我发现效果很好的一种,coding.debuntu.org /…欢呼!:)
乔DF

4
// Here is the code for unicode strings!


int mystrstr(wchar_t *txt1,wchar_t *txt2)
{
    wchar_t *posstr=wcsstr(txt1,txt2);
    if(posstr!=NULL)
    {
        return (posstr-txt1);
    }else
    {
        return -1;
    }
}

// assume: supplied buff is enough to hold generated text
void StringReplace(wchar_t *buff,wchar_t *txt1,wchar_t *txt2)
{
    wchar_t *tmp;
    wchar_t *nextStr;
    int pos;

    tmp=wcsdup(buff);

    pos=mystrstr(tmp,txt1);
    if(pos!=-1)
    {
        buff[0]=0;
        wcsncpy(buff,tmp,pos);
        buff[pos]=0;

        wcscat(buff,txt2);

        nextStr=tmp+pos+wcslen(txt1);

        while(wcslen(nextStr)!=0)
        {
            pos=mystrstr(nextStr,txt1);

            if(pos==-1)
            {
                wcscat(buff,nextStr);
                break;
            }

            wcsncat(buff,nextStr,pos);
            wcscat(buff,txt2);

            nextStr=nextStr+pos+wcslen(txt1);   
        }
    }

    free(tmp);
}

3

creativeandcritical.net上的repl_str()函数快速可靠。该页面上还包括一个宽字符串变体repl_wcs(),它可以通过辅助函数与Unicode字符串一起使用,包括以UTF-8编码的Unicode字符串-演示代码从页面链接。迟来的完整披露:我是该页面及其功能的作者。


3
快速可靠,但存在巨大的内存泄漏。
MightyPork,2014年

3
我不知道怎么可能。只有一个malloc,并且指示调用者在不再需要时释放内存。你可以再详细一点吗?
Laird

@Lairdpos_cache = realloc(pos_cache
PSkocik '16

@PSkocik自@MightyPork投诉以来,该功能已升级,但即使现在它具有pos_cache的附加malloc / realloc,我也看不到避免free(pos_cache);at函数结束的代码路径。
Laird

@Laird realloc可能会失败。如果是这样,它将返回NULL并保留旧指针不变。p = realloc(p, x)将上失败,重写一个有效的堆指针pNULL,如果这p是你该堆对象仅供参考,您现在已经泄露了。这是一个经典的新手错误。
PSkocik

3

我发现大多数建议的功能难以理解-所以我想到了:

static char *dull_replace(const char *in, const char *pattern, const char *by)
{
    size_t outsize = strlen(in) + 1;
    // TODO maybe avoid reallocing by counting the non-overlapping occurences of pattern
    char *res = malloc(outsize);
    // use this to iterate over the output
    size_t resoffset = 0;

    char *needle;
    while (needle = strstr(in, pattern)) {
        // copy everything up to the pattern
        memcpy(res + resoffset, in, needle - in);
        resoffset += needle - in;

        // skip the pattern in the input-string
        in = needle + strlen(pattern);

        // adjust space for replacement
        outsize = outsize - strlen(pattern) + strlen(by);
        res = realloc(res, outsize);

        // copy the pattern
        memcpy(res + resoffset, by, strlen(by));
        resoffset += strlen(by);
    }

    // copy the remaining input
    strcpy(res + resoffset, in);

    return res;
}

输出必须是自由的


2

您可以使用此功能(注释说明其工作原理):

void strreplace(char *string, const char *find, const char *replaceWith){
    if(strstr(string, replaceWith) != NULL){
        char *temporaryString = malloc(strlen(strstr(string, find) + strlen(find)) + 1);
        strcpy(temporaryString, strstr(string, find) + strlen(find));    //Create a string with what's after the replaced part
        *strstr(string, find) = '\0';    //Take away the part to replace and the part after it in the initial string
        strcat(string, replaceWith);    //Concat the first part of the string with the part to replace with
        strcat(string, temporaryString);    //Concat the first part of the string with the part after the replaced part
        free(temporaryString);    //Free the memory to avoid memory leaks
    }
}

1

这是我根据这些要求创建的:

  1. 无论花样长短,都应更换花样。

  2. 不要使用任何malloc(显式或隐式)从本质上避免内存泄漏。

  3. 替换任意数量的模式。

  4. 允许替换字符串的子字符串等于搜索字符串。

  5. 不必检查Line数组的大小足以容纳替换项。例如,除非调用者知道该行的大小足以容纳新字符串,否则此方法不起作用。

/* returns number of strings replaced.
*/
int replacestr(char *line, const char *search, const char *replace)
{
   int count;
   char *sp; // start of pattern

   //printf("replacestr(%s, %s, %s)\n", line, search, replace);
   if ((sp = strstr(line, search)) == NULL) {
      return(0);
   }
   count = 1;
   int sLen = strlen(search);
   int rLen = strlen(replace);
   if (sLen > rLen) {
      // move from right to left
      char *src = sp + sLen;
      char *dst = sp + rLen;
      while((*dst = *src) != '\0') { dst++; src++; }
   } else if (sLen < rLen) {
      // move from left to right
      int tLen = strlen(sp) - sLen;
      char *stop = sp + rLen;
      char *src = sp + sLen + tLen;
      char *dst = sp + rLen + tLen;
      while(dst >= stop) { *dst = *src; dst--; src--; }
   }
   memcpy(sp, replace, rLen);

   count += replacestr(sp + rLen, search, replace);

   return(count);
}

我们乐意接受任何有关改进此代码的建议。只需发表评论,我将对其进行测试。


1

您可以使用strrep()

char * strrep(const char * cadena,const char * strf,const char * strr)

strrep(字符串替换)。在“ cadena”中将“ strf”替换为“ strr”,并返回新字符串。使用strrep后,您需要在代码中释放返回的字符串。

参数cadena带有文本的字符串。strf要查找的文本。strr替换文本。

返回值替换后更新的文本。

可以在https://github.com/ipserc/strrep上找到项目


0

使用字符串的就地修改并假定由行指向的缓冲区足够大,可以容纳结果字符串,从而解决了fann95的响应。

static void replacestr(char *line, const char *search, const char *replace)
{
     char *sp;

     if ((sp = strstr(line, search)) == NULL) {
         return;
     }
     int search_len = strlen(search);
     int replace_len = strlen(replace);
     int tail_len = strlen(sp+search_len);

     memmove(sp+replace_len,sp+search_len,tail_len+1);
     memcpy(sp, replace, replace_len);
}

0

到那里....这是char xchar y字符串替换每次出现的功能str

char *zStrrep(char *str, char x, char y){
    char *tmp=str;
    while(*tmp)
        if(*tmp == x)
            *tmp++ = y; /* assign first, then incement */
        else
            *tmp++;

    *tmp='\0';
    return str;
}

一个示例用法可能是

  Exmaple Usage
        char s[]="this is a trial string to test the function.";
        char x=' ', y='_';
        printf("%s\n",zStrrep(s,x,y));

  Example Output
        this_is_a_trial_string_to_test_the_function.

该函数来自我在Github上维护字符串库,非常欢迎您查看其他可用函数,甚至为代码做贡献:)

https://github.com/fnoyanisi/zString

编辑:@siride是正确的,上面的函数仅替换chars。刚刚写了这个,代替了字符串。

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

/* replace every occurance of string x with string y */
char *zstring_replace_str(char *str, const char *x, const char *y){
    char *tmp_str = str, *tmp_x = x, *dummy_ptr = tmp_x, *tmp_y = y;
    int len_str=0, len_y=0, len_x=0;

    /* string length */
    for(; *tmp_y; ++len_y, ++tmp_y)
        ;

    for(; *tmp_str; ++len_str, ++tmp_str)
        ;

    for(; *tmp_x; ++len_x, ++tmp_x)
        ;

    /* Bounds check */
    if (len_y >= len_str)
        return str;

    /* reset tmp pointers */
    tmp_y = y;
    tmp_x = x;

    for (tmp_str = str ; *tmp_str; ++tmp_str)
        if(*tmp_str == *tmp_x) {
            /* save tmp_str */
            for (dummy_ptr=tmp_str; *dummy_ptr == *tmp_x; ++tmp_x, ++dummy_ptr)
                if (*(tmp_x+1) == '\0' && ((dummy_ptr-str+len_y) < len_str)){
                /* Reached end of x, we got something to replace then!
                * Copy y only if there is enough room for it
                */
                    for(tmp_y=y; *tmp_y; ++tmp_y, ++tmp_str)
                        *tmp_str = *tmp_y;
            }
        /* reset tmp_x */
        tmp_x = x;
        }

    return str;
}

int main()
{
    char s[]="Free software is a matter of liberty, not price.\n"
             "To understand the concept, you should think of 'free' \n"
             "as in 'free speech', not as in 'free beer'";

    printf("%s\n\n",s);
    printf("%s\n",zstring_replace_str(s,"ree","XYZ"));
    return 0;
}

下面是输出

Free software is a matter of liberty, not price.
To understand the concept, you should think of 'free' 
as in 'free speech', not as in 'free beer'

FXYZ software is a matter of liberty, not price.
To understand the concept, you should think of 'fXYZ' 
as in 'fXYZ speech', not as in 'fXYZ beer'

这仅替换单个字符,而不替换子字符串。
siride '16

0
/*замена символа в строке*/
char* replace_char(char* str, char in, char out) {
    char * p = str;

    while(p != '\0') {
        if(*p == in)
            *p == out;
        ++p;
    }

    return str;
}

str为null时segfault
Code_So1dier

0
DWORD ReplaceString(__inout PCHAR source, __in DWORD dwSourceLen, __in const char* pszTextToReplace, __in const char* pszReplaceWith)
{
    DWORD dwRC = NO_ERROR;
    PCHAR foundSeq = NULL;
    PCHAR restOfString = NULL;
    PCHAR searchStart = source;
    size_t szReplStrcLen = strlen(pszReplaceWith), szRestOfStringLen = 0, sztextToReplaceLen = strlen(pszTextToReplace), remainingSpace = 0, dwSpaceRequired = 0;
    if (strcmp(pszTextToReplace, "") == 0)
        dwRC = ERROR_INVALID_PARAMETER;
    else if (strcmp(pszTextToReplace, pszReplaceWith) != 0)
    {
        do
        {
            foundSeq = strstr(searchStart, pszTextToReplace);
            if (foundSeq)
            {
                szRestOfStringLen = (strlen(foundSeq) - sztextToReplaceLen) + 1;
                remainingSpace = dwSourceLen - (foundSeq - source);
                dwSpaceRequired = szReplStrcLen + (szRestOfStringLen);
                if (dwSpaceRequired > remainingSpace)
                {
                    dwRC = ERROR_MORE_DATA;
                }

                else
                {
                    restOfString = CMNUTIL_calloc(szRestOfStringLen, sizeof(CHAR));
                    strcpy_s(restOfString, szRestOfStringLen, foundSeq + sztextToReplaceLen);

                    strcpy_s(foundSeq, remainingSpace, pszReplaceWith);
                    strcat_s(foundSeq, remainingSpace, restOfString);
                }

                CMNUTIL_free(restOfString);
                searchStart = foundSeq + szReplStrcLen; //search in the remaining str. (avoid loops when replWith contains textToRepl 
            }
        } while (foundSeq && dwRC == NO_ERROR);
    }
    return dwRC;
}

0
char *replace(const char*instring, const char *old_part, const char *new_part)
{

#ifndef EXPECTED_REPLACEMENTS
    #define EXPECTED_REPLACEMENTS 100
#endif

    if(!instring || !old_part || !new_part)
    {
        return (char*)NULL;
    }

    size_t instring_len=strlen(instring);
    size_t new_len=strlen(new_part);
    size_t old_len=strlen(old_part);
    if(instring_len<old_len || old_len==0)
    {
        return (char*)NULL;
    }

    const char *in=instring;
    const char *found=NULL;
    size_t count=0;
    size_t out=0;
    size_t ax=0;
    char *outstring=NULL;

    if(new_len> old_len )
    {
        size_t Diff=EXPECTED_REPLACEMENTS*(new_len-old_len);
        size_t outstring_len=instring_len + Diff;
        outstring =(char*) malloc(outstring_len); 
        if(!outstring){
            return (char*)NULL;
        }
        while((found = strstr(in, old_part))!=NULL)
        {
            if(count==EXPECTED_REPLACEMENTS)
            {
                outstring_len+=Diff;
                if((outstring=realloc(outstring,outstring_len))==NULL)
                {
                     return (char*)NULL;
                }
                count=0;
            }
            ax=found-in;
            strncpy(outstring+out,in,ax);
            out+=ax;
            strncpy(outstring+out,new_part,new_len);
            out+=new_len;
            in=found+old_len;
            count++;
        }
    }
    else
    {
        outstring =(char*) malloc(instring_len);
        if(!outstring){
            return (char*)NULL;
        }
        while((found = strstr(in, old_part))!=NULL)
        {
            ax=found-in;
            strncpy(outstring+out,in,ax);
            out+=ax;
            strncpy(outstring+out,new_part,new_len);
            out+=new_len;
            in=found+old_len;
        }
    }
    ax=(instring+instring_len)-in;
    strncpy(outstring+out,in,ax);
    out+=ax;
    outstring[out]='\0';

    return outstring;
}

0

仅当ur字符串具有新长度的额外空间时,此功能才起作用

void replace_str(char *str,char *org,char *rep)
{
    char *ToRep = strstr(str,org);
    char *Rest = (char*)malloc(strlen(ToRep));
    strcpy(Rest,((ToRep)+strlen(org)));

    strcpy(ToRep,rep);
    strcat(ToRep,Rest);

    free(Rest);
}

这仅替换第一次出现


0

这是我的,它是自包含的,通用的,高效的,它可以根据每次递归的需要增加或缩小缓冲区

void strreplace(char *src, char *str, char *rep)
{
    char *p = strstr(src, str);
    if (p)
    {
        int len = strlen(src)+strlen(rep)-strlen(str);
        char r[len];
        memset(r, 0, len);
        if ( p >= src ){
            strncpy(r, src, p-src);
            r[p-src]='\0';
            strncat(r, rep, strlen(rep));
            strncat(r, p+strlen(str), p+strlen(str)-src+strlen(src));
            strcpy(src, r);
            strreplace(p+strlen(rep), str, rep);
        }
    }
}

0

这是我的,将它们全部设为char *,这使调用更加容易...

char *strrpc(char *str,char *oldstr,char *newstr){
    char bstr[strlen(str)];
    memset(bstr,0,sizeof(bstr));
    int i;
    for(i = 0;i < strlen(str);i++){
        if(!strncmp(str+i,oldstr,strlen(oldstr))){
            strcat(bstr,newstr);
            i += strlen(oldstr) - 1;
        }else{
                strncat(bstr,str + i,1);
            }
    }

    strcpy(str,bstr);
    return str;
}

0

仅使用来自string.h的strlen

对不起我的英语不好

char * str_replace(char * text,char * rep, char * repw){//text -> to replace in it | rep -> replace | repw -> replace with
    int replen = strlen(rep),repwlen = strlen(repw),count;//some constant variables
    for(int i=0;i<strlen(text);i++){//search for the first character from rep in text
        if(text[i] == rep[0]){//if it found it
            count = 1;//start searching from the next character to avoid repetition
            for(int j=1;j<replen;j++){
                if(text[i+j] == rep[j]){//see if the next character in text is the same as the next in the rep if not break
                    count++;
                }else{
                    break;
                }
            }
            if(count == replen){//if count equals to the lenght of the rep then we found the word that we want to replace in the text
                if(replen < repwlen){
                    for(int l = strlen(text);l>i;l--){//cuz repwlen greater than replen we need to shift characters to the right to make space for the replacement to fit
                        text[l+repwlen-replen] = text[l];//shift by repwlen-replen
                    }
                }
                if(replen > repwlen){
                    for(int l=i+replen-repwlen;l<strlen(text);l++){//cuz replen greater than repwlen we need to shift the characters to the left
                        text[l-(replen-repwlen)] = text[l];//shift by replen-repwlen
                    }
                    text[strlen(text)-(replen-repwlen)] = '\0';//get rid of the last unwanted characters
                }
                for(int l=0;l<repwlen;l++){//replace rep with repwlen
                    text[i+l] = repw[l];
                }
                if(replen != repwlen){
                    i+=repwlen-1;//pass to the next character | try text "y" ,rep "y",repw "yy" without this line to understand
                }
            }
        }
    }
    return text;
}

如果您想使用strlen代码来避免调用string.h

int strlen(char * string){//use this code to avoid calling string.h
    int lenght = 0;
    while(string[lenght] != '\0'){
        lenght++;
    }
    return lenght;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.