Quelle est la fonction de remplacer une sous-chaîne d'une chaîne en C?
94
Étant donné une char *chaîne ( ), je veux trouver toutes les occurrences d'une sous-chaîne et les remplacer par une chaîne alternative. Je ne vois aucune fonction simple qui permette d'y parvenir <string.h>.
Je doute que vous puissiez le faire de manière mutable
user44511
Réponses:
87
L'optimiseur doit éliminer la plupart des variables locales. Le pointeur tmp est là pour s'assurer que strcpy n'a pas à parcourir la chaîne pour trouver le null. tmp pointe vers la fin du résultat après chaque appel. (Voir l'algorithme de Shlemiel le peintre pour savoir pourquoi strcpy peut être ennuyeux.)
// 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: utilisez à la size_tplace de intpour des tailles et des index arbitraires d'objets / chaînes. Aussi, quel est le but strcpy(tmp, orig);à la toute fin? Cela semble faux.
Alexey Frunze
@Alex, le dernier strcpy (tmp, orig) copie la dernière partie de la chaîne vers la destination. Ex: replace ("abab", "a", "c") à la fin de la boucle, result contient, "cbc" et orig pointe vers le dernier "b" de "abab". Le dernier strcpy ajoute le "b" de sorte que la chaîne renvoyée soit "cbcb". S'il n'y a plus rien à copier, orig doit pointer vers l'ASCIIZ de la chaîne d'entrée.
jmucchiello
simplification: vous pouvez remplacer cette première forboucle par for (count = 1; ins = strstr(ins + rep_len, rep); ++count) {}, puis tmpn'est utilisée que pour l'écriture.
rampion
1
char * done = replace ("abcdefghijkl", "bc", "yz"); faire des trucs(); gratuit (fait);
jmucchiello
2
Soyez averti que cette fonction renvoie NULL s'il n'y a pas d'occurrences à remplacer (if (! (Ins = strstr (orig, rep))) return NULL;). Vous ne pouvez pas simplement utiliser la sortie, vous devez vérifier si la sortie est NULL et si tel est le cas, utilisez la chaîne d'origine (ne copiez pas simplement le pointeur vers la chaîne de résultat car free (result) libère alors la chaîne d'origine). L'utilisation est plus simple si la chaîne d'entrée est simplement copiée dans la chaîne de sortie s'il n'y a rien à remplacer.
Adversus
18
Ceci n'est pas fourni dans la bibliothèque C standard car, avec seulement un char *, vous ne pouvez pas augmenter la mémoire allouée à la chaîne si la chaîne de remplacement est plus longue que la chaîne remplacée.
Vous pouvez le faire en utilisant std :: string plus facilement, mais même là, aucune fonction ne le fera à votre place.
1 / strlen (char *) + 1 n'est pas nécessairement égal à la taille de stockage. 2 / Il y a beaucoup de N versions de fonctions string qui reçoivent un paramètre supplémentaire de taille de tampon donc il n'y a aucune raison pour laquelle il n'y aurait pas de snreplace (). 3 / il pourrait y avoir une fonction de remplacement sur place et non de remplacement sur place. 4 / Comment penses-tu que Sprintf fonctionne? On lui donne un argument char * et il n'est pas nécessaire d'augmenter l'allocation de mémoire de celui-ci, donc aucune raison pour qu'un remplacement ne puisse pas fonctionner aussi ... (bien que C ait une mauvaise conception de «chaîne», et la taille du tampon devrait toujours être passée avec le pointeur => snprintf)
Steven Spark
12
Il n'y en a pas.
Vous devez créer le vôtre en utilisant quelque chose comme strstr et strcat ou strcpy.
Où sont stockées les collections de fans de fonctions souvent utilisées? Il y a sûrement déjà une bibliothèque pour cela ....
Pacerier
1
strcat()est une mauvaise suggestion.
Iharob Al Asimi
11
Vous pouvez créer votre propre fonction de remplacement en utilisant strstr pour trouver les sous-chaînes et strncpy pour copier en parties dans un nouveau tampon.
À moins que ce que vous voulez replace_withsoit de la même longueur que ce que vous voulez replace, il est probablement préférable d'utiliser un nouveau tampon pour copier la nouvelle chaîne.
Comme les chaînes en C ne peuvent pas se développer dynamiquement, la substitution en place ne fonctionnera généralement pas. Par conséquent, vous devez allouer de l'espace pour une nouvelle chaîne qui a suffisamment de place pour votre substitution, puis copier les parties de l'original plus la substitution dans la nouvelle chaîne. Pour copier les parties, vous utiliseriez strncpy .
La taille de la mémoire tampon peut être plus grande que la strlen, la chaîne de remplacement peut être plus petite que la chaîne remplacée ... vous n'avez donc pas besoin d'allouer de la mémoire pour effectuer le remplacement. (Également sur les microcontrôleurs, vous n'aurez peut-être pas une mémoire infinie et vous devrez peut-être effectuer un remplacement sur place. Copier tout dans un nouveau tampon n'est peut-être pas la bonne solution pour tout le monde ...)
Steven Spark
8
Voici un exemple de code qui le fait.
#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;}
Cela fonctionne, mais c'est un peu bogué, mais merci quand même! : D en voici un que j'ai trouvé qui fonctionne très bien, coding.debuntu.org/ ... bravo ! :)
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 fonction repl_str () sur creativeandcritical.net est rapide et fiable. Cette page contient également une variante de chaîne large, repl_wcs () , qui peut être utilisée avec des chaînes Unicode, y compris celles encodées en UTF-8, via des fonctions d'assistance - le code de démonstration est lié à partir de la page. Divulgation complète tardive: je suis l'auteur de cette page et des fonctions qu'elle contient.
rapide et fiable, mais a une énorme fuite de mémoire.
MightyPork
3
Je ne vois pas comment cela pourrait. Il n'y a qu'un seul malloc et l'appelant est invité à libérer la mémoire lorsqu'elle n'est plus nécessaire. Pourriez-vous être plus précis?
Laird
@Lairdpos_cache = realloc(pos_cache
PSkocik
@PSkocik La fonction a été mise à jour depuis la plainte de @MightyPork mais même si elle a maintenant ce malloc / realloc supplémentaire pour pos_cache, je ne vois pas de chemin de code qui évite la free(pos_cache);fin de la fonction at.
Laird le
@Laird reallocpeut échouer. Si c'est le cas, il retourne NULLet laisse l'ancien pointeur intact. p = realloc(p, x)va, en cas d'échec, réécrire un pointeur de tas valide pavec NULL, et si pc'était votre seule référence à cet objet de tas, vous l'avez maintenant divulgué. C'est une erreur de débutant classique.
PSkocik
3
Je trouve que la plupart des fonctions proposées sont difficiles à comprendre - alors j'ai trouvé ceci:
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;}
Vous pouvez utiliser cette fonction (les commentaires expliquent comment cela fonctionne):
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}}
Voici celui que j'ai créé en fonction de ces exigences:
Remplacez le motif, qu'il soit long ou court.
N'utilisez aucun malloc (explicite ou implicite) pour éviter intrinsèquement les fuites de mémoire.
Remplacez n'importe quel nombre d'occurrences de motif.
Tolérez la chaîne de remplacement ayant une sous-chaîne égale à la chaîne de recherche.
Il n'est pas nécessaire de vérifier que le Line array est de taille suffisante pour contenir le remplacement. Par exemple, cela ne fonctionne que si l'appelant sait que la ligne est de taille suffisante pour contenir la nouvelle chaîne.
/* 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);}
Toutes les suggestions pour améliorer ce code sont acceptées avec joie. Postez simplement le commentaire et je vais le tester.
strrep (remplacement de chaîne). Remplace «strf» par «strr» dans «cadena» et renvoie la nouvelle chaîne. Vous devez libérer la chaîne retournée dans votre code après avoir utilisé strrep.
Paramètres cadena La chaîne avec le texte. strf Le texte à rechercher. strr Le texte de remplacement.
un correctif à la réponse de fann95, en utilisant la modification sur place de la chaîne, et en supposant que le tampon pointé par la ligne est suffisamment grand pour contenir la chaîne résultante.
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.
EDIT: @siride a raison, la fonction ci-dessus ne remplace que les caractères. Je viens d'écrire celui-ci, qui remplace les chaînes de caractères.
#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;}
Et ci-dessous est la sortie
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 vous voulez que le code strlen évite d'appeler string.h
int strlen(char* string){//use this code to avoid calling string.hint lenght =0;while(string[lenght]!='\0'){
lenght++;}return lenght;}
Réponses:
L'optimiseur doit éliminer la plupart des variables locales. Le pointeur tmp est là pour s'assurer que strcpy n'a pas à parcourir la chaîne pour trouver le null. tmp pointe vers la fin du résultat après chaque appel. (Voir l'algorithme de Shlemiel le peintre pour savoir pourquoi strcpy peut être ennuyeux.)
la source
size_t
place deint
pour des tailles et des index arbitraires d'objets / chaînes. Aussi, quel est le butstrcpy(tmp, orig);
à la toute fin? Cela semble faux.for
boucle parfor (count = 1; ins = strstr(ins + rep_len, rep); ++count) {}
, puistmp
n'est utilisée que pour l'écriture.Ceci n'est pas fourni dans la bibliothèque C standard car, avec seulement un char *, vous ne pouvez pas augmenter la mémoire allouée à la chaîne si la chaîne de remplacement est plus longue que la chaîne remplacée.
Vous pouvez le faire en utilisant std :: string plus facilement, mais même là, aucune fonction ne le fera à votre place.
la source
Il n'y en a pas.
Vous devez créer le vôtre en utilisant quelque chose comme strstr et strcat ou strcpy.
la source
strcat()
est une mauvaise suggestion.Vous pouvez créer votre propre fonction de remplacement en utilisant strstr pour trouver les sous-chaînes et strncpy pour copier en parties dans un nouveau tampon.
À moins que ce que vous voulez
replace_with
soit de la même longueur que ce que vous voulezreplace
, il est probablement préférable d'utiliser un nouveau tampon pour copier la nouvelle chaîne.la source
Comme les chaînes en C ne peuvent pas se développer dynamiquement, la substitution en place ne fonctionnera généralement pas. Par conséquent, vous devez allouer de l'espace pour une nouvelle chaîne qui a suffisamment de place pour votre substitution, puis copier les parties de l'original plus la substitution dans la nouvelle chaîne. Pour copier les parties, vous utiliseriez strncpy .
la source
Voici un exemple de code qui le fait.
la source
la source
La fonction repl_str () sur creativeandcritical.net est rapide et fiable. Cette page contient également une variante de chaîne large, repl_wcs () , qui peut être utilisée avec des chaînes Unicode, y compris celles encodées en UTF-8, via des fonctions d'assistance - le code de démonstration est lié à partir de la page. Divulgation complète tardive: je suis l'auteur de cette page et des fonctions qu'elle contient.
la source
pos_cache = realloc(pos_cache
free(pos_cache);
fin de la fonction at.realloc
peut échouer. Si c'est le cas, il retourneNULL
et laisse l'ancien pointeur intact.p = realloc(p, x)
va, en cas d'échec, réécrire un pointeur de tas validep
avecNULL
, et sip
c'était votre seule référence à cet objet de tas, vous l'avez maintenant divulgué. C'est une erreur de débutant classique.Je trouve que la plupart des fonctions proposées sont difficiles à comprendre - alors j'ai trouvé ceci:
la sortie doit être libre
la source
Vous pouvez utiliser cette fonction (les commentaires expliquent comment cela fonctionne):
la source
Voici celui que j'ai créé en fonction de ces exigences:
Remplacez le motif, qu'il soit long ou court.
N'utilisez aucun malloc (explicite ou implicite) pour éviter intrinsèquement les fuites de mémoire.
Remplacez n'importe quel nombre d'occurrences de motif.
Tolérez la chaîne de remplacement ayant une sous-chaîne égale à la chaîne de recherche.
Il n'est pas nécessaire de vérifier que le Line array est de taille suffisante pour contenir le remplacement. Par exemple, cela ne fonctionne que si l'appelant sait que la ligne est de taille suffisante pour contenir la nouvelle chaîne.
Toutes les suggestions pour améliorer ce code sont acceptées avec joie. Postez simplement le commentaire et je vais le tester.
la source
Vous pouvez utiliser strrep ()
char * strrep (const char * cadena, const char * strf, const char * strr)
strrep (remplacement de chaîne). Remplace «strf» par «strr» dans «cadena» et renvoie la nouvelle chaîne. Vous devez libérer la chaîne retournée dans votre code après avoir utilisé strrep.
Paramètres cadena La chaîne avec le texte. strf Le texte à rechercher. strr Le texte de remplacement.
Renvoie Le texte mis à jour avec le remplacement.
Le projet peut être trouvé à https://github.com/ipserc/strrep
la source
un correctif à la réponse de fann95, en utilisant la modification sur place de la chaîne, et en supposant que le tampon pointé par la ligne est suffisamment grand pour contenir la chaîne résultante.
la source
Voilà ... c'est la fonction pour remplacer chaque occurrence de
char x
avec unechar y
chaîne de caractèresstr
Un exemple d'utilisation pourrait être
La fonction provient d'une bibliothèque de chaînes que je maintiens sur Github , vous êtes plus que bienvenu pour jeter un coup d'œil aux autres fonctions disponibles ou même contribuer au code :)
https://github.com/fnoyanisi/zString
EDIT: @siride a raison, la fonction ci-dessus ne remplace que les caractères. Je viens d'écrire celui-ci, qui remplace les chaînes de caractères.
Et ci-dessous est la sortie
la source
la source
la source
la source
Cette fonction ne fonctionne que si votre chaîne a un espace supplémentaire pour la nouvelle longueur
Cela remplace uniquement la première occurrence
la source
Voici le mien, il est autonome et polyvalent, ainsi qu'efficace, il augmente ou réduit les tampons au besoin à chaque récursivité
la source
Voici le mien, faites-les tous char *, ce qui facilite les appels ...
la source
Utiliser uniquement strlen de string.h
Désolé pour mon anglais
si vous voulez que le code strlen évite d'appeler string.h
la source