¿Qué función es reemplazar una subcadena de una cadena en C?
94
Dada una char *cadena ( ), quiero encontrar todas las apariciones de una subcadena y reemplazarlas con una cadena alternativa. No veo ninguna función simple que logre esto en <string.h>.
El optimizador debería eliminar la mayoría de las variables locales. El puntero tmp está ahí para asegurarse de que strcpy no tenga que recorrer la cadena para encontrar el nulo. tmp apunta al final del resultado después de cada llamada. (Consulte el algoritmo del pintor de Shlemiel para saber por qué strcpy puede ser molesto).
// You must free the result if result is non-NULL.char*str_replace(char*orig,char*rep,char*with){char*result;// the return stringchar*ins;// the next insert pointchar*tmp;// variesint 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 repint count;// number of replacements// sanity checks and initializationif(!orig ||!rep)return NULL;
len_rep = strlen(rep);if(len_rep ==0)return NULL;// empty rep causes infinite loop during countif(!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: se usa en size_tlugar de intpara objetos arbitrarios / tamaños de cadena e índices en ellos. Además, ¿cuál es el propósito strcpy(tmp, orig);al final? Parece mal.
Alexey Frunze
@Alex, la última strcpy (tmp, orig) copia la última parte de la cadena en el destino. Por ejemplo: reemplace ("abab", "a", "c") al final del ciclo, el resultado contiene, "cbc" y orig apunta a la última "b" en "abab". La última strcpy agrega la "b" por lo que la cadena devuelta es "cbcb". Si no queda nada para copiar, orig debería apuntar al ASCIIZ de la cadena de entrada.
jmucchiello
simplificación: puede reemplazar ese primer forbucle con for (count = 1; ins = strstr(ins + rep_len, rep); ++count) {}, luego tmpsolo se usa para escribir.
Tenga en cuenta que esta función devuelve NULL si no hay ocurrencias para reemplazar (if (! (Ins = strstr (orig, rep))) return NULL;). No puede simplemente usar la salida, debe verificar si la salida es NULL y, si es así, use la cadena original (no solo copie el puntero a la cadena de resultado porque free (resultado) libera la cadena original). El uso es más sencillo si la cadena de entrada solo se copia en la cadena de salida si no hay nada que reemplazar.
Adversus
18
Esto no se proporciona en la biblioteca C estándar porque, dado solo un carácter *, no puede aumentar la memoria asignada a la cadena si la cadena de reemplazo es más larga que la cadena que se reemplaza.
Puede hacer esto usando std :: string más fácilmente, pero incluso allí, ninguna función lo hará por usted.
1 / strlen (char *) + 1 no es necesariamente igual al tamaño de almacenamiento. 2 / Hay muchas N versiones de funciones de cadena que reciben un parámetro de tamaño de búfer adicional, por lo que no hay ninguna razón por la que no pueda haber un snreplace (). 3 / podría haber una función de reemplazo in situ y no una función de reemplazo in situ. 4 / ¿cómo crees que funciona sprintf? Se le da un argumento char * y no es necesario aumentar la asignación de memoria, por lo que no hay razón para que un reemplazo no funcione también ... (aunque C tiene un mal diseño de "cadena" y el tamaño del búfer siempre debe pasarse con el puntero => snprintf)
Steven Spark
12
No hay uno.
Necesitarías rodar el tuyo usando algo como strstr y strcat o strcpy.
¿Dónde se almacenan las colecciones de ventiladores de funciones de uso frecuente? Seguramente ya hay una biblioteca para ello ....
Pacerier
1
strcat()es una mala sugerencia.
Iharob Al Asimi
11
Puede crear su propia función de reemplazo usando strstr para encontrar las subcadenas y strncpy para copiar en partes a un nuevo búfer.
A menos que lo que desee replace_withtenga la misma longitud que lo que desea replace, probablemente sea mejor usar un nuevo búfer para copiar la nueva cadena.
Como las cadenas en C no pueden crecer dinámicamente en el lugar, la sustitución generalmente no funcionará. Por lo tanto, debe asignar espacio para una nueva cadena que tenga suficiente espacio para su sustitución y luego copiar las partes del original más la sustitución en la nueva cadena. Para copiar las partes, usaría strncpy .
El tamaño del búfer podría ser mayor que el strlen, la cadena de reemplazo podría ser más pequeña que la cadena reemplazada ... por lo tanto, no necesita asignar memoria para realizar el reemplazo. (También en los microcontroladores es posible que no tenga memoria infinita, y es posible que deba realizar el reemplazo en su lugar. Copiar todo en un nuevo búfer puede no ser la solución adecuada para todos ...)
Steven Spark
8
Aquí hay un código de muestra que lo hace.
#include<string.h>#include<stdlib.h>char* replace(charconst*const original,charconst*const pattern,charconst*const replacement
){size_tconst replen = strlen(replacement);size_tconst patlen = strlen(pattern);size_tconst orilen = strlen(original);size_t patcnt =0;constchar* oriptr;constchar* patloc;// find how many times the pattern occurs in the original stringfor(oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen){
patcnt++;}{// allocate memory for the new stringsize_tconst 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 patternchar* retptr = returned;for(oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen){size_tconst 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);}}return0;}
Funciona, pero tiene un poco de errores, ¡pero gracias de todos modos! : D aquí hay uno que encontré que funciona muy bien, coding.debuntu.org/… ¡ salud ! :)
Joe 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 textvoidStringReplace(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);}
La función repl_str () en creativeandcritical.net es rápida y confiable. También se incluye en esa página una variante de cadena amplia, repl_wcs () , que se puede usar con cadenas Unicode, incluidas las codificadas en UTF-8, a través de funciones auxiliares; el código de demostración está vinculado desde la página. Revelación completa tardía: soy el autor de esa página y las funciones que contiene.
rápido y confiable, pero tiene una gran pérdida de memoria.
MightyPork
3
No veo cómo podría hacerlo. Solo hay un malloc y se le indica a la persona que llama que libere la memoria cuando ya no sea necesaria. ¿Podrías ser más específico?
Laird
@Lairdpos_cache = realloc(pos_cache
PSkocik
@PSkocik La función se ha actualizado desde la queja de @MightyPork, pero aunque ahora tiene ese malloc / realloc adicional para pos_cache, no puedo ver una ruta de código que evite el free(pos_cache);final de la función en.
Laird
@Laird reallocpuede fallar. Si lo hace, vuelve NULLy deja intacto el puntero antiguo. p = realloc(p, x), en caso de falla, reescribirá un puntero de montón válido pcon NULL, y si esa pera su única referencia a ese objeto de montón, ahora lo ha filtrado. Es un error clásico de novato.
PSkocik
3
Encuentro que la mayoría de las funciones propuestas son difíciles de entender, así que se me ocurrió esto:
staticchar*dull_replace(constchar*in,constchar*pattern,constchar*by){size_t outsize = strlen(in)+1;// TODO maybe avoid reallocing by counting the non-overlapping occurences of patternchar*res = malloc(outsize);// use this to iterate over the outputsize_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;}
Puede utilizar esta función (los comentarios explican cómo funciona):
void strreplace(char*string,constchar*find,constchar*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}}
Reemplace el patrón independientemente de si es largo o más corto.
No utilice ningún malloc (explícito o implícito) para evitar intrínsecamente pérdidas de memoria.
Reemplace cualquier número de apariciones de patrón.
Tolere que la cadena de reemplazo tenga una subcadena igual a la cadena de búsqueda.
No es necesario comprobar que el Line array tenga el tamaño suficiente para contener el reemplazo. Por ejemplo, esto no funciona a menos que la persona que llama sepa que la línea tiene el tamaño suficiente para contener la nueva cadena.
/* returns number of strings replaced.
*/int replacestr(char*line,constchar*search,constchar*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 leftchar*src = sp + sLen;char*dst = sp + rLen;while((*dst =*src)!='\0'){ dst++; src++;}}elseif(sLen < rLen){// move from left to rightint 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);}
Cualquier sugerencia para mejorar este código se acepta con gusto. Simplemente publique el comentario y lo probaré.
strrep (Reemplazo de cadena). Reemplaza 'strf' con 'strr' en 'cadena' y devuelve la nueva cadena. Debe liberar la cadena devuelta en su código después de usar strrep.
Parámetros cadena La cadena con el texto. strf El texto a buscar. strr El texto de reemplazo.
una solución a la respuesta de fann95, usando la modificación en el lugar de la cadena y asumiendo que el búfer al que apunta la línea es lo suficientemente grande para contener la cadena resultante.
ExmapleUsagechar s[]="this is a trial string to test the function.";char x=' ', y='_';
printf("%s\n",zStrrep(s,x,y));ExampleOutput
this_is_a_trial_string_to_test_the_function.
EDITAR: @siride tiene razón, la función anterior reemplaza solo los caracteres. Acabo de escribir este, que reemplaza las cadenas de caracteres.
#include<stdio.h>#include<stdlib.h>/* replace every occurance of string x with string y */char*zstring_replace_str(char*str,constchar*x,constchar*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"));return0;}
Y abajo está la salida
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'
char* str_replace(char* text,char* rep,char* repw){//text -> to replace in it | rep -> replace | repw -> replace withint replen = strlen(rep),repwlen = strlen(repw),count;//some constant variablesfor(int i=0;i<strlen(text);i++){//search for the first character from rep in textif(text[i]== rep[0]){//if it found it
count =1;//start searching from the next character to avoid repetitionfor(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 textif(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;}
si desea un código strlen para evitar llamar a string.h
int strlen(char* string){//use this code to avoid calling string.hint lenght =0;while(string[lenght]!='\0'){
lenght++;}return lenght;}
Respuestas:
El optimizador debería eliminar la mayoría de las variables locales. El puntero tmp está ahí para asegurarse de que strcpy no tenga que recorrer la cadena para encontrar el nulo. tmp apunta al final del resultado después de cada llamada. (Consulte el algoritmo del pintor de Shlemiel para saber por qué strcpy puede ser molesto).
fuente
size_t
lugar deint
para objetos arbitrarios / tamaños de cadena e índices en ellos. Además, ¿cuál es el propósitostrcpy(tmp, orig);
al final? Parece mal.for
bucle confor (count = 1; ins = strstr(ins + rep_len, rep); ++count) {}
, luegotmp
solo se usa para escribir.Esto no se proporciona en la biblioteca C estándar porque, dado solo un carácter *, no puede aumentar la memoria asignada a la cadena si la cadena de reemplazo es más larga que la cadena que se reemplaza.
Puede hacer esto usando std :: string más fácilmente, pero incluso allí, ninguna función lo hará por usted.
fuente
No hay uno.
Necesitarías rodar el tuyo usando algo como strstr y strcat o strcpy.
fuente
strcat()
es una mala sugerencia.Puede crear su propia función de reemplazo usando strstr para encontrar las subcadenas y strncpy para copiar en partes a un nuevo búfer.
A menos que lo que desee
replace_with
tenga la misma longitud que lo que deseareplace
, probablemente sea mejor usar un nuevo búfer para copiar la nueva cadena.fuente
Como las cadenas en C no pueden crecer dinámicamente en el lugar, la sustitución generalmente no funcionará. Por lo tanto, debe asignar espacio para una nueva cadena que tenga suficiente espacio para su sustitución y luego copiar las partes del original más la sustitución en la nueva cadena. Para copiar las partes, usaría strncpy .
fuente
Aquí hay un código de muestra que lo hace.
fuente
fuente
La función repl_str () en creativeandcritical.net es rápida y confiable. También se incluye en esa página una variante de cadena amplia, repl_wcs () , que se puede usar con cadenas Unicode, incluidas las codificadas en UTF-8, a través de funciones auxiliares; el código de demostración está vinculado desde la página. Revelación completa tardía: soy el autor de esa página y las funciones que contiene.
fuente
pos_cache = realloc(pos_cache
free(pos_cache);
final de la función en.realloc
puede fallar. Si lo hace, vuelveNULL
y deja intacto el puntero antiguo.p = realloc(p, x)
, en caso de falla, reescribirá un puntero de montón válidop
conNULL
, y si esap
era su única referencia a ese objeto de montón, ahora lo ha filtrado. Es un error clásico de novato.Encuentro que la mayoría de las funciones propuestas son difíciles de entender, así que se me ocurrió esto:
la salida debe ser libre
fuente
Puede utilizar esta función (los comentarios explican cómo funciona):
fuente
Aquí está el que creé en base a estos requisitos:
Reemplace el patrón independientemente de si es largo o más corto.
No utilice ningún malloc (explícito o implícito) para evitar intrínsecamente pérdidas de memoria.
Reemplace cualquier número de apariciones de patrón.
Tolere que la cadena de reemplazo tenga una subcadena igual a la cadena de búsqueda.
No es necesario comprobar que el Line array tenga el tamaño suficiente para contener el reemplazo. Por ejemplo, esto no funciona a menos que la persona que llama sepa que la línea tiene el tamaño suficiente para contener la nueva cadena.
Cualquier sugerencia para mejorar este código se acepta con gusto. Simplemente publique el comentario y lo probaré.
fuente
Puedes usar strrep ()
char * strrep (const char * cadena, const char * strf, const char * strr)
strrep (Reemplazo de cadena). Reemplaza 'strf' con 'strr' en 'cadena' y devuelve la nueva cadena. Debe liberar la cadena devuelta en su código después de usar strrep.
Parámetros cadena La cadena con el texto. strf El texto a buscar. strr El texto de reemplazo.
Devuelve El texto actualizado con el reemplazo.
El proyecto se puede encontrar en https://github.com/ipserc/strrep
fuente
una solución a la respuesta de fann95, usando la modificación en el lugar de la cadena y asumiendo que el búfer al que apunta la línea es lo suficientemente grande para contener la cadena resultante.
fuente
Hay que ir .... esta es la función de sustituir cada ocurrencia de
char x
lachar y
dentro de la cadena de caracteresstr
Un ejemplo de uso podría ser
La función es de una biblioteca de cadenas que mantengo en Github , eres más que bienvenido a echar un vistazo a otras funciones disponibles o incluso contribuir al código :)
https://github.com/fnoyanisi/zString
EDITAR: @siride tiene razón, la función anterior reemplaza solo los caracteres. Acabo de escribir este, que reemplaza las cadenas de caracteres.
Y abajo está la salida
fuente
fuente
fuente
fuente
Esta función solo funciona si tu cadena tiene espacio adicional para una nueva longitud
Esto solo reemplaza la primera aparición
fuente
Aquí va el mío, es autónomo y versátil, además de eficiente, aumenta o reduce los búferes según sea necesario en cada recursión
fuente
Aquí va el mío, hazlos todos char *, lo que facilita las llamadas ...
fuente
Usando solo strlen de string.h
Lo siento por mi ingles
si desea un código strlen para evitar llamar a string.h
fuente