Si vous pouvez modifier la chaîne:
// Note: This function returns a pointer to a substring of the original string.
// If the given string was allocated dynamically, the caller must not overwrite
// that pointer with the returned value, since the original pointer must be
// deallocated using the same allocator with which it was allocated. The return
// value must NOT be deallocated using free() etc.
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
Si vous ne pouvez pas modifier la chaîne, vous pouvez utiliser essentiellement la même méthode:
// Stores the trimmed input string into the given output buffer, which must be
// large enough to store the result. If it is too small, the output is
// truncated.
size_t trimwhitespace(char *out, size_t len, const char *str)
{
if(len == 0)
return 0;
const char *end;
size_t out_size;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
{
*out = 0;
return 1;
}
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
end++;
// Set output size to minimum of trimmed string length and buffer size minus 1
out_size = (end - str) < len-1 ? (end - str) : len-1;
// Copy trimmed string and add null terminator
memcpy(out, str, out_size);
out[out_size] = 0;
return out_size;
}
Adam Rosenfield
la source
str
est une variable locale et sa modification ne change pas le pointeur d'origine transmis. Les appels de fonction en C sont toujours pass-by-value, jamais pass-by-reference.free()
fonction. Bien au contraire, j'ai conçu cela pour éviter le besoin d'allocation de mémoire pour plus d'efficacité. Si l'adresse transmise a été allouée dynamiquement, l'appelant est toujours responsable de la libération de cette mémoire, et l'appelant doit être sûr de ne pas écraser cette valeur par la valeur renvoyée ici.isspace
enunsigned char
, sinon vous invoquez un comportement non défini.En voici un qui déplace la chaîne dans la première position de votre tampon. Vous pouvez souhaiter ce comportement pour que si vous allouiez dynamiquement la chaîne, vous puissiez toujours la libérer sur le même pointeur que trim () renvoie:
Test d'exactitude:
Le fichier source était trim.c. Compilé avec 'cc -Wall trim.c -o trim'.
la source
isspace
enunsigned char
, sinon vous invoquez un comportement non défini.isspace()
alors pourquoi y aurait-il une différence entre" "
et"\n"
? J'ai ajouté des tests unitaires pour les nouvelles lignes et il semble OK pour moi ... ideone.com/bbVmqo*(endp + 1) = '\0';
. L'exemple de test sur la réponse utilise un buffer de 64 qui évite ce problème.Ma solution. La chaîne doit être modifiable. L'avantage par rapport à certaines des autres solutions est qu'il déplace la partie non-espace au début afin que vous puissiez continuer à utiliser l'ancien pointeur, au cas où vous auriez à le libérer () plus tard.
Cette version crée une copie de la chaîne avec strndup () au lieu de la modifier sur place. strndup () nécessite _GNU_SOURCE, donc peut-être que vous devez créer votre propre strndup () avec malloc () et strncpy ().
la source
trim()
invoque UB sis
est""
comme le premierisspace()
appelisspace(p[-1])
etp[-1]
ne fait pas nécessairement référence à un emplacement légal.isspace
enunsigned char
, sinon vous invoquez un comportement non défini.if(l==0)return;
pour éviter uneVoici ma mini bibliothèque C pour rogner à gauche, à droite, les deux, tous, en place et séparés, et rogner un ensemble de caractères spécifiés (ou un espace blanc par défaut).
contenu de strlib.h:
contenu de strlib.c:
La seule routine principale fait tout. Il coupe en place si src == dst , sinon, il fonctionne comme les
strcpy
routines. Il supprime un ensemble de caractères spécifié dans la chaîne de délimitation, ou un espace blanc si nul. Il coupe à gauche, à droite, les deux et tout (comme tr). Il n'y a pas grand-chose à faire et il ne parcourt la chaîne qu'une seule fois. Certaines personnes pourraient se plaindre que la coupe à droite commence à gauche, cependant, aucun strlen n'est nécessaire qui commence de toute façon à gauche. (D'une manière ou d'une autre, vous devez arriver à la fin de la chaîne pour obtenir les bons ajustements, vous pouvez donc aussi bien faire le travail au fur et à mesure.) Il peut y avoir des arguments à faire sur le pipelining et la taille du cache, etc. . Étant donné que la solution fonctionne de gauche à droite et n'itère qu'une seule fois, elle peut être étendue pour fonctionner également sur les flux. Limitations: cela ne fonctionne pas sur les chaînes unicode .la source
dtab[*d]
ne pas jeter*d
àunsigned int
avant de l' utiliser comme un index de tableau. Sur un système avec un caractère signé, cela liradtab[-127]
ce qui provoquera des bogues et éventuellement des plantages.dtab[*delim++]
carchar
les valeurs d'index doivent être convertiesunsigned char
. Le code suppose 8 bitschar
.delim
doit être déclaré commeconst char *
.dtab[0xFF & (unsigned int)*d]
serait plus clair quedtab[(unsigned char)*d]
. Le code fonctionne sur les chaînes encodées en UTF-8, mais ne supprime pas les séquences d'espacement non ASCII.Voici ma tentative d'une fonction de garniture en place simple mais correcte.
la source
while ((end >= begin) && isspace(str[end]))
pour empêcher UB lorsquestr is
"". Prevents
str [-1] `.isspace
enunsigned char
, sinon vous invoquez un comportement non défini.<ctype.h>
sont destinées à fonctionner avec des ints, qui représentent soitunsigned char
soit la valeur spécialeEOF
. Voir stackoverflow.com/q/7131026/225757 .Tard à la soirée
Caractéristiques:
1. Coupez le début rapidement, comme dans un certain nombre d'autres réponses.
2. Après être allé à la fin, coupez la droite avec un seul test par boucle. Comme @ jfm3, mais fonctionne pour une chaîne entièrement composée d'espaces blancs)
3. Pour éviter un comportement indéfini lorsque
char
est signéchar
, transtypez*s
enunsigned char
.@chqrlie a commenté ce qui précède ne déplace pas la chaîne tronquée . Faire cela....
la source
Voici une solution similaire à la routine de modification sur place @ adam-rosenfields, mais sans recourir inutilement à strlen (). Comme @jkramer, la chaîne est ajustée à gauche dans le tampon afin que vous puissiez libérer le même pointeur. Pas optimal pour les grosses chaînes car il n'utilise pas memmove. Inclut les opérateurs ++ / - que @ jfm3 mentionne. Tests unitaires basés sur FCTX inclus.
la source
Un autre, avec une ligne faisant le vrai travail:
la source
%n
spécificateur de conversion, et à la fin, c'est juste plus simple de le faire à la main, j'en ai peur.Je n'ai pas aimé la plupart de ces réponses parce qu'elles faisaient une ou plusieurs des choses suivantes ...
Voici ma version:
la source
isspace
enunsigned char
, sinon vous invoquez un comportement non défini.while (isspace((unsigned char) *szWrite)) szWrite++;
empêcherait cela. Le code copie également tout l'espace blanc de fin.*szWrite = *szRead
lorsque les pointeurs ne sont pas égaux sauterait les écritures dans ce cas, mais nous avons ajouté une autre comparaison / branche. Avec les CPU / MMU / BP modernes, je n'ai aucune idée si ce contrôle serait une perte ou un gain. Avec des processeurs et des architectures de mémoire plus simples, il est moins coûteux de simplement faire la copie et d'ignorer la comparaison.Très tard à la fête ...
Solution de numérisation vers l'avant en un seul passage sans retour en arrière. Chaque caractère de la chaîne source est testé exactement
une foisdeux fois. (Donc, cela devrait être plus rapide que la plupart des autres solutions ici, surtout si la chaîne source a beaucoup d'espaces de fin.)Cela inclut deux solutions, l'une pour copier et découper une chaîne source dans une autre chaîne de destination, et l'autre pour découper la chaîne source sur place. Les deux fonctions utilisent le même code.
La chaîne (modifiable) est déplacée sur place, de sorte que le pointeur d'origine vers elle reste inchangé.
la source
'\0'
et ensuite testés avecisspace()
. Il semble inutile de tester tous les personnages avecisspace()
. Le retour en arrière à partir de la fin de la chaîne devrait être plus efficace pour les cas non pathologiques.trim()
D'ACCORD. Cas d'angle:trim2(char *d, const char *s)
a des problèmes lors dud,s
chevauchement ets < d
.trim()
se comporter? Vous demandez de couper et de copier une chaîne dans la mémoire occupée par la chaîne elle-même. Contrairement àmemmove()
cela, cela nécessite de déterminer la longueur de la chaîne source avant d'effectuer le découpage lui-même, ce qui nécessite de scanner la chaîne entière une fois supplémentaire. Mieux vaut écrire unertrim2()
fonction différente qui sait copier la source vers la destination vers l'arrière, et prend probablement un argument de longueur de chaîne source supplémentaire.Je ne suis pas sûr de ce que vous considérez comme «indolore».
Les cordes C sont assez douloureuses. Nous pouvons trouver la première position de caractère non-espace de manière triviale:
Nous pouvons trouver la dernière position de caractère non-espace avec deux mouvements triviaux similaires:
(Je vous ai épargné la peine d'utiliser les opérateurs
*
et++
en même temps.)La question maintenant est que faites-vous avec cela? Le type de données à portée de main n'est pas vraiment un gros résumé robuste
String
auquel il est facile de penser, mais au contraire à peine plus qu'un tableau d'octets de stockage. Faute d'un type de données robuste, il est impossible d'écrire une fonction qui fera la même chose que lachomp
fonction de PHperytonby . Que renverrait une telle fonction en C?la source
do { q--; } ...
de savoir*q != 0
.Utilisez une bibliothèque de chaînes , par exemple:
... comme vous dites que c'est un problème "commun", oui vous devez inclure un #include ou quelque chose comme ça et il n'est pas inclus dans la libc mais n'inventez pas votre propre travail de hack en stockant des pointeurs aléatoires et size_t de cette façon ne mène qu'à débordements de tampon.
la source
Si vous utilisez
glib
, vous pouvez utiliser g_strstripla source
Juste pour que cela continue de grandir, une autre option avec une chaîne modifiable:
la source
strlen()
renvoie unsize_t
qui peut dépasser la plage deint
. l'espace blanc n'est pas limité au caractère d'espace. Enfin mais le plus important: Comportement indéfini activéstrcpy(string, string + i * sizeof(char));
car les tableaux source et destination se chevauchent. Utilisez à lamemmove()
place destrcpy()
.while (isspace((int)string[i])) string[i--] = '\0';
peut boucler au-delà du début de la chaîne. Vous devez combiner cette boucle avec les lignes précédentes et suivantes et écrirewhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
elle ne pointait pas vers l'octet nul de fin et vousend = ++i;
aviez toujours un problème pour les chaînes contenant tous les caractères d'espacement. Je viens de corriger le code.Je sais qu'il y a beaucoup de réponses, mais je poste ma réponse ici pour voir si ma solution est assez bonne.
la source
isspace(*str)
UB quand*str < 0
.size_t n
est bonne, mais l'interface n'informe en aucun cas l'appelant lorsqu'iln
est trop petit pour une chaîne complète coupée. Considérertrim(out, 12, "delete data not")
Le moyen le plus simple d'ignorer les espaces de début dans une chaîne est, à mon humble avis,
la source
" foo bar "
.Ok, c'est mon point de vue sur la question. Je crois que c'est la solution la plus concise qui modifie la chaîne en place (
free
fonctionnera) et évite tout UB. Pour les petites chaînes, c'est probablement plus rapide qu'une solution impliquant memmove.la source
b > str
test n'est nécessaire qu'une seule fois.*b = 0;
nécessaire une seule fois.isspace
aide à couper tous les espaces blancs.strndup
pour créer un nouveau tampon de chaîne en excluant les espaces.la source
strndup()
ne fait pas partie du standard C mais seulement Posix. Mais comme il est assez facile à mettre en œuvre, ce n'est pas un gros problème.trim_space("")
revientNULL
. Je m'attendrais à un pointeur vers""
.int len;
devrait êtresize_t len;
.isspace(in[len - 1])
UB quandin[len - 1] < 0
.while (isspace((unsigned char) *in) in++;
avantlen = strlen(in);
serait plus efficace que le plus tardwhile(len && *in && isspace(*in)) ++in, --len;
Personnellement, je roulerais le mien. Vous pouvez utiliser strtok, mais vous devez faire attention de le faire (en particulier si vous supprimez les caractères principaux) pour savoir ce qu'est la mémoire.
Se débarrasser des espaces de fin est facile et assez sûr, car vous pouvez simplement mettre un 0 au-dessus du dernier espace, en comptant à rebours à partir de la fin. Se débarrasser des espaces de premier plan signifie déplacer les choses. Si vous voulez le faire en place (probablement judicieux), vous pouvez simplement continuer à tout reculer d'un caractère jusqu'à ce qu'il n'y ait plus d'espace de début. Ou, pour être plus efficace, vous pouvez trouver l'index du premier caractère non espace et tout décaler de ce nombre. Ou, vous pouvez simplement utiliser un pointeur vers le premier caractère non espace (mais vous devez alors faire attention de la même manière que vous le faites avec strtok).
la source
la source
Un peu tard dans le match, mais je vais jeter mes routines dans la mêlée. Ils ne sont probablement pas les plus efficaces, mais je pense qu'ils sont corrects et simples (en
rtrim()
repoussant les limites de la complexité):la source
char
argument enisspace()
pour(unsigned char)
éviter un comportement indéfini sur des valeurs potentiellement négatives. Évitez également de déplacer la chaîne siltrim()
elle n'est pas nécessaire.La plupart des réponses à ce jour font l'une des choses suivantes:
strlen()
abord, en effectuant un deuxième passage sur toute la chaîne.Cette version ne fait qu'un seul passage et ne revient pas en arrière. Par conséquent, il peut fonctionner mieux que les autres, mais seulement s'il est courant d'avoir des centaines d'espaces de fin (ce qui n'est pas inhabituel lorsqu'il s'agit de la sortie d'une requête SQL.)
la source
strspn()
etstrcspn()
dans une boucle serrée. Ceci est très inefficace et la surcharge éclipsera l'avantage non prouvé de la passe avant unique.strlen()
est généralement étendu en ligne avec un code très efficace, ce n'est pas un réel problème. Le découpage du début et de la fin de la chaîne sera beaucoup plus rapide que de tester la blancheur de chaque caractère de la chaîne, même dans le cas particulier des chaînes avec très peu ou pas de caractères non blancs.C'est la mise en œuvre la plus courte possible à laquelle je puisse penser:
la source
char *trim(char *s) { char *p = s, *e = s + strlen(s); while (e > s && isspace((unsigned char)e[-1])) { *--e = '\0'; } while (isspace((unsigned char)*p)) { p++; } if (p > s) { memmove(s, p, e + 1 - p); } return s; }
Ces fonctions modifieront le tampon d'origine, donc si elles sont allouées dynamiquement, le pointeur d'origine peut être libéré.
la source
rstrip()
invoque un comportement non défini sur la chaîne vide.lstrip()
est inutilement lent sur une chaîne avec une longue partie initiale de caractères d'espacement.isspace()
ne doit pas recevoir d'char
argument car il invoque un comportement non défini sur des valeurs négatives différentes deEOF
.Que pensez-vous de l'utilisation de la fonction StrTrim définie dans l'en-tête Shlwapi.h.? C'est simple plutôt définissant par vous-même.
Les détails peuvent être trouvés sur:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Si vous avez
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
Cela donnera
ausCaptain
comme"GeorgeBailey"
non"GeorgeBailey "
.la source
Pour couper mes cordes des deux côtés, j'utilise l'ancien mais le gooody;) Il peut couper n'importe quoi avec ascii moins d'un espace, ce qui signifie que les caractères de contrôle seront également coupés!
la source
size_t
place deunsigned int
. Le code a beaucoup de tests redondants et appelle un comportement indéfinistrncpy(strData,&strData[S],L)
car les tableaux source et destination se chevauchent. Utilisez à lamemmove()
place destrncpy()
.Je n'inclus que du code car le code publié jusqu'à présent semble sous-optimal (et je n'ai pas encore le représentant à commenter.)
strndup()
est une extension GNU. Si vous ne l'avez pas ou quelque chose d'équivalent, lancez le vôtre. Par exemple:la source
isspace(0)
est défini comme étant faux, vous pouvez simplifier les deux fonctions. Déplacez également l'memmove()
intérieur duif
bloc.Ici, j'utilise l'allocation de mémoire dynamique pour couper la chaîne d'entrée à la fonction trimStr. Tout d'abord, nous trouvons combien de caractères non vides existent dans la chaîne d'entrée. Ensuite, nous allouons un tableau de caractères avec cette taille et en prenant soin du caractère terminé par null. Lorsque nous utilisons cette fonction, nous devons libérer la mémoire à l'intérieur de la fonction principale.
la source
Voici comment je le fais. Il coupe la chaîne en place, donc pas de souci de désallouer une chaîne retournée ou de perdre le pointeur vers une chaîne allouée. Ce n'est peut-être pas la réponse la plus courte possible, mais elle devrait être claire pour la plupart des lecteurs.
la source
la source