Risposte:
Se puoi modificare la stringa:
// 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;
}
Se non è possibile modificare la stringa, è possibile utilizzare sostanzialmente lo stesso metodo:
// 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;
}
str
è una variabile locale e la sua modifica non cambia il puntatore originale che viene passato. Le chiamate di funzione in C sono sempre pass-by-value, mai pass-by-reference.
free()
funzione. Al contrario, ho progettato questo per evitare la necessità di allocazione della memoria per l'efficienza. Se l'indirizzo passato è stato allocato in modo dinamico, il chiamante è comunque responsabile della liberazione di tale memoria e il chiamante deve essere sicuro di non sovrascrivere quel valore con il valore restituito qui.
isspace
a unsigned char
, altrimenti si richiama un comportamento indefinito.
Eccone uno che sposta la stringa nella prima posizione del buffer. Potresti desiderare questo comportamento in modo che se hai allocato dinamicamente la stringa, puoi comunque liberarla sullo stesso puntatore che restituisce ():
char *trim(char *str)
{
size_t len = 0;
char *frontp = str;
char *endp = NULL;
if( str == NULL ) { return NULL; }
if( str[0] == '\0' ) { return str; }
len = strlen(str);
endp = str + len;
/* Move the front and back pointers to address the first non-whitespace
* characters from each end.
*/
while( isspace((unsigned char) *frontp) ) { ++frontp; }
if( endp != frontp )
{
while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
}
if( frontp != str && endp == frontp )
*str = '\0';
else if( str + len - 1 != endp )
*(endp + 1) = '\0';
/* Shift the string so that it starts at str so that if it's dynamically
* allocated, we can still free it on the returned pointer. Note the reuse
* of endp to mean the front of the string buffer now.
*/
endp = str;
if( frontp != str )
{
while( *frontp ) { *endp++ = *frontp++; }
*endp = '\0';
}
return str;
}
Verifica della correttezza:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* Paste function from above here. */
int main()
{
/* The test prints the following:
[nothing to trim] -> [nothing to trim]
[ trim the front] -> [trim the front]
[trim the back ] -> [trim the back]
[ trim front and back ] -> [trim front and back]
[ trim one char front and back ] -> [trim one char front and back]
[ trim one char front] -> [trim one char front]
[trim one char back ] -> [trim one char back]
[ ] -> []
[ ] -> []
[a] -> [a]
[] -> []
*/
char *sample_strings[] =
{
"nothing to trim",
" trim the front",
"trim the back ",
" trim front and back ",
" trim one char front and back ",
" trim one char front",
"trim one char back ",
" ",
" ",
"a",
"",
NULL
};
char test_buffer[64];
char comparison_buffer[64];
size_t index, compare_pos;
for( index = 0; sample_strings[index] != NULL; ++index )
{
// Fill buffer with known value to verify we do not write past the end of the string.
memset( test_buffer, 0xCC, sizeof(test_buffer) );
strcpy( test_buffer, sample_strings[index] );
memcpy( comparison_buffer, test_buffer, sizeof(comparison_buffer));
printf("[%s] -> [%s]\n", sample_strings[index],
trim(test_buffer));
for( compare_pos = strlen(comparison_buffer);
compare_pos < sizeof(comparison_buffer);
++compare_pos )
{
if( test_buffer[compare_pos] != comparison_buffer[compare_pos] )
{
printf("Unexpected change to buffer @ index %u: %02x (expected %02x)\n",
compare_pos, (unsigned char) test_buffer[compare_pos], (unsigned char) comparison_buffer[compare_pos]);
}
}
}
return 0;
}
Il file di origine era trim.c. Compilato con 'cc -Wall trim.c -o trim'.
isspace
a unsigned char
, altrimenti si richiama un comportamento indefinito.
isspace()
quindi perché dovrebbe esserci una differenza tra " "
e "\n"
? Ho aggiunto unit test per nuove linee e sembra OK per me ... ideone.com/bbVmqo
*(endp + 1) = '\0';
. Il test di esempio sulla risposta utilizza un buffer di 64 che evita questo problema.
La mia soluzione. La stringa deve essere modificabile. Il vantaggio rispetto ad alcune delle altre soluzioni è che sposta la parte non spaziale all'inizio in modo da poter continuare a utilizzare il vecchio puntatore, nel caso in cui sia necessario liberarlo () in un secondo momento.
void trim(char * s) {
char * p = s;
int l = strlen(p);
while(isspace(p[l - 1])) p[--l] = 0;
while(* p && isspace(* p)) ++p, --l;
memmove(s, p, l + 1);
}
Questa versione crea una copia della stringa con strndup () invece di modificarla in posizione. strndup () richiede _GNU_SOURCE, quindi forse devi creare il tuo strndup () con malloc () e strncpy ().
char * trim(char * s) {
int l = strlen(s);
while(isspace(s[l - 1])) --l;
while(* s && isspace(* s)) ++s, --l;
return strndup(s, l);
}
trim()
invoca UB se s
è ""
come la prima isspace()
chiamata sarebbe isspace(p[-1])
e p[-1]
non fa necessariamente riferimento a una posizione legale.
isspace
a unsigned char
, altrimenti si richiama un comportamento indefinito.
if(l==0)return;
per evitare str di lunghezza zero
Ecco la mia mini libreria C per tagliare a sinistra, a destra, entrambi, tutti, in posizione e separati, e tagliare un set di caratteri specificati (o uno spazio bianco per impostazione predefinita).
#ifndef STRLIB_H_
#define STRLIB_H_ 1
enum strtrim_mode_t {
STRLIB_MODE_ALL = 0,
STRLIB_MODE_RIGHT = 0x01,
STRLIB_MODE_LEFT = 0x02,
STRLIB_MODE_BOTH = 0x03
};
char *strcpytrim(char *d, // destination
char *s, // source
int mode,
char *delim
);
char *strtriml(char *d, char *s);
char *strtrimr(char *d, char *s);
char *strtrim(char *d, char *s);
char *strkill(char *d, char *s);
char *triml(char *s);
char *trimr(char *s);
char *trim(char *s);
char *kill(char *s);
#endif
#include <strlib.h>
char *strcpytrim(char *d, // destination
char *s, // source
int mode,
char *delim
) {
char *o = d; // save orig
char *e = 0; // end space ptr.
char dtab[256] = {0};
if (!s || !d) return 0;
if (!delim) delim = " \t\n\f";
while (*delim)
dtab[*delim++] = 1;
while ( (*d = *s++) != 0 ) {
if (!dtab[0xFF & (unsigned int)*d]) { // Not a match char
e = 0; // Reset end pointer
} else {
if (!e) e = d; // Found first match.
if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) )
continue;
}
d++;
}
if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches.
*e = 0;
}
return o;
}
// perhaps these could be inlined in strlib.h
char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); }
char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); }
char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); }
char *strkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); }
char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); }
char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); }
char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); }
char *kill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); }
L'unica routine principale fa tutto. Si regola se src == dst , altrimenti funziona come le strcpy
routine. Taglia un set di caratteri specificato nel delim di stringao spazio bianco se nullo. Rifila a sinistra, a destra, entrambi e tutti (come tr). Non c'è molto da fare e scorre la stringa una sola volta. Alcune persone potrebbero lamentarsi del fatto che tagliare a destra inizia a sinistra, tuttavia, non è necessario alcun rinforzo che inizia comunque a sinistra. (In un modo o nell'altro devi arrivare alla fine della stringa per i giusti trim, quindi potresti anche fare il lavoro mentre procedi.) Potrebbero essere fatti degli argomenti su pipeline e dimensioni della cache e simili - chissà . Poiché la soluzione funziona da sinistra a destra e viene ripetuta una sola volta, può essere espansa per funzionare anche su stream. Limitazioni: non funziona su stringhe unicode .
dtab[*d]
non espressi *d
a unsigned int
prima di utilizzarla come un indice di matrice. Su un sistema con caratteri firmati, questo si leggerà fino a dtab[-127]
che causerà bug e probabilmente arresti anomali.
dtab[*delim++]
perché char
è necessario eseguire il cast dei valori dell'indice unsigned char
. Il codice assume 8 bit char
. delim
dovrebbe essere dichiarato come const char *
. dtab[0xFF & (unsigned int)*d]
sarebbe più chiaro come dtab[(unsigned char)*d]
. Il codice funziona su stringhe codificate UTF-8, ma non rimuove le sequenze di spaziatura non ASCII.
Ecco il mio tentativo di una funzione di trim sul posto semplice ma corretta.
void trim(char *str)
{
int i;
int begin = 0;
int end = strlen(str) - 1;
while (isspace((unsigned char) str[begin]))
begin++;
while ((end >= begin) && isspace((unsigned char) str[end]))
end--;
// Shift all characters back to the start of the string array.
for (i = begin; i <= end; i++)
str[i - begin] = str[i];
str[i - begin] = '\0'; // Null terminate string.
}
while ((end >= begin) && isspace(str[end]))
per impedire UB quando str is
"" . Prevents
str [-1] `.
isspace
a unsigned char
, altrimenti si richiama un comportamento indefinito.
<ctype.h>
hanno lo scopo di lavorare con ints, che rappresentano uno unsigned char
o il valore speciale EOF
. Vedi stackoverflow.com/q/7131026/225757 .
In ritardo per la festa
Caratteristiche:
1. Taglia rapidamente l'inizio, come in una serie di altre risposte.
2. Dopo essere arrivato alla fine, tagliare la destra con solo 1 test per loop. Come @ jfm3, ma funziona per una stringa tutta vuota.
3. Per evitare comportamenti indefiniti quando char
è un segno, eseguire il char
cast *s
su unsigned char
.
Gestione del carattere "In tutti i casi l'argomento è un
int
, il cui valore deve essere rappresentabile comeunsigned char
o deve essere uguale al valore della macroEOF
. Se l'argomento ha un altro valore, il comportamento non è definito." C11 §7.4 1
#include <ctype.h>
// Return a pointer to the trimmed string
char *string_trim_inplace(char *s) {
while (isspace((unsigned char) *s)) s++;
if (*s) {
char *p = s;
while (*p) p++;
while (isspace((unsigned char) *(--p)));
p[1] = '\0';
}
// If desired, shift the trimmed string
return s;
}
@chqrlie ha commentato quanto sopra non sposta la stringa tagliata. Fare così....
// Return a pointer to the (shifted) trimmed string
char *string_trim_inplace(char *s) {
char *original = s;
size_t len = 0;
while (isspace((unsigned char) *s)) {
s++;
}
if (*s) {
char *p = s;
while (*p) p++;
while (isspace((unsigned char) *(--p)));
p[1] = '\0';
// len = (size_t) (p - s); // older errant code
len = (size_t) (p - s + 1); // Thanks to @theriver
}
return (s == original) ? s : memmove(original, s, len + 1);
}
Ecco una soluzione simile alla routine di modifica sul posto di @ adam-rosenfields ma senza ricorrere inutilmente a strlen (). Come @jkramer, la stringa viene regolata a sinistra all'interno del buffer in modo da poter liberare lo stesso puntatore. Non ottimale per stringhe di grandi dimensioni poiché non utilizza memmove. Include gli operatori ++ / - citati da @ jfm3. Test unitari basati su FCTX inclusi.
#include <ctype.h>
void trim(char * const a)
{
char *p = a, *q = a;
while (isspace(*q)) ++q;
while (*q) *p++ = *q++;
*p = '\0';
while (p > a && isspace(*--p)) *p = '\0';
}
/* See http://fctx.wildbearsoftware.com/ */
#include "fct.h"
FCT_BGN()
{
FCT_QTEST_BGN(trim)
{
{ char s[] = ""; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = " "; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = "\t"; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = "a"; trim(s); fct_chk_eq_str("a", s); } // NOP
{ char s[] = "abc"; trim(s); fct_chk_eq_str("abc", s); } // NOP
{ char s[] = " a"; trim(s); fct_chk_eq_str("a", s); } // Leading
{ char s[] = " a c"; trim(s); fct_chk_eq_str("a c", s); } // Leading
{ char s[] = "a "; trim(s); fct_chk_eq_str("a", s); } // Trailing
{ char s[] = "a c "; trim(s); fct_chk_eq_str("a c", s); } // Trailing
{ char s[] = " a "; trim(s); fct_chk_eq_str("a", s); } // Both
{ char s[] = " a c "; trim(s); fct_chk_eq_str("a c", s); } // Both
// Villemoes pointed out an edge case that corrupted memory. Thank you.
// http://stackoverflow.com/questions/122616/#comment23332594_4505533
{
char s[] = "a "; // Buffer with whitespace before s + 2
trim(s + 2); // Trim " " containing only whitespace
fct_chk_eq_str("", s + 2); // Ensure correct result from the trim
fct_chk_eq_str("a ", s); // Ensure preceding buffer not mutated
}
// doukremt suggested I investigate this test case but
// did not indicate the specific behavior that was objectionable.
// http://stackoverflow.com/posts/comments/33571430
{
char s[] = " foobar"; // Shifted across whitespace
trim(s); // Trim
fct_chk_eq_str("foobar", s); // Leading string is correct
// Here is what the algorithm produces:
char r[16] = { 'f', 'o', 'o', 'b', 'a', 'r', '\0', ' ',
' ', 'f', 'o', 'o', 'b', 'a', 'r', '\0'};
fct_chk_eq_int(0, memcmp(s, r, sizeof(s)));
}
}
FCT_QTEST_END();
}
FCT_END();
Un altro, con una riga che fa il vero lavoro:
#include <stdio.h>
int main()
{
const char *target = " haha ";
char buf[256];
sscanf(target, "%s", buf); // Trimming on both sides occurs here
printf("<%s>\n", buf);
}
%n
conversione, e alla fine è più semplice farlo a mano, temo.
La maggior parte di queste risposte non mi è piaciuta perché hanno fatto una o più delle seguenti ...
Ecco la mia versione:
void fnStrTrimInPlace(char *szWrite) {
const char *szWriteOrig = szWrite;
char *szLastSpace = szWrite, *szRead = szWrite;
int bNotSpace;
// SHIFT STRING, STARTING AT FIRST NON-SPACE CHAR, LEFTMOST
while( *szRead != '\0' ) {
bNotSpace = !isspace((unsigned char)(*szRead));
if( (szWrite != szWriteOrig) || bNotSpace ) {
*szWrite = *szRead;
szWrite++;
// TRACK POINTER TO LAST NON-SPACE
if( bNotSpace )
szLastSpace = szWrite;
}
szRead++;
}
// TERMINATE AFTER LAST NON-SPACE (OR BEGINNING IF THERE WAS NO NON-SPACE)
*szLastSpace = '\0';
}
isspace
a unsigned char
, altrimenti si richiama un comportamento indefinito.
while (isspace((unsigned char) *szWrite)) szWrite++;
lo impedirebbe. Il codice copia anche tutto lo spazio bianco finale.
*szWrite = *szRead
quando i puntatori non sono uguali salterebbe le scritture in quel caso, ma poi abbiamo aggiunto un altro confronto / ramo. Con la moderna CPU / MMU / BP, non ho idea se quel controllo sarebbe una perdita o un guadagno. Con processori e architetture di memoria più semplici, è più economico fare semplicemente la copia e saltare il confronto.
Molto tardi alla festa ...
Soluzione di scansione in avanti a passaggio singolo senza backtracking. Ogni carattere nella stringa di origine viene testato esattamente una volta due volte. (Quindi dovrebbe essere più veloce della maggior parte delle altre soluzioni qui, specialmente se la stringa di origine ha molti spazi finali.)
Ciò include due soluzioni, una per copiare e tagliare una stringa sorgente in un'altra stringa di destinazione e l'altra per tagliare la stringa sorgente in posizione. Entrambe le funzioni usano lo stesso codice.
La stringa (modificabile) viene spostata sul posto, quindi il puntatore originale rimane invariato.
#include <stddef.h>
#include <ctype.h>
char * trim2(char *d, const char *s)
{
// Sanity checks
if (s == NULL || d == NULL)
return NULL;
// Skip leading spaces
const unsigned char * p = (const unsigned char *)s;
while (isspace(*p))
p++;
// Copy the string
unsigned char * dst = (unsigned char *)d; // d and s can be the same
unsigned char * end = dst;
while (*p != '\0')
{
if (!isspace(*dst++ = *p++))
end = dst;
}
// Truncate trailing spaces
*end = '\0';
return d;
}
char * trim(char *s)
{
return trim2(s, s);
}
'\0'
e quindi testati con isspace()
. Sembra inutile testare tutti i personaggi con isspace()
. Il backtracking dall'estremità della stringa dovrebbe essere più efficiente per i casi non patologici.
trim()
OK. Caso d'angolo: trim2(char *d, const char *s)
ha problemi quando si d,s
sovrappongono e s < d
.
trim()
comportarsi? Stai chiedendo di tagliare e copiare una stringa nella memoria occupata dalla stringa stessa. Diversamente memmove()
, ciò richiede la determinazione della lunghezza della stringa sorgente prima di eseguire il trim stesso, il che richiede la scansione dell'intera stringa una volta in più. Meglio scrivere una rtrim2()
funzione diversa che sappia copiare l'origine nella destinazione all'indietro e probabilmente accetta un argomento aggiuntivo della lunghezza della stringa di origine.
Non sono sicuro di cosa consideri "indolore".
Le stringhe C sono piuttosto dolorose. Possiamo trovare banalmente la prima posizione di carattere non spaziale:
while (isspace (* p)) p ++;
Possiamo trovare l'ultima posizione del personaggio non di spazio bianco con due mosse banali simili:
while (* q) q ++; do {q--; } while (isspace (* q));
(Ti ho risparmiato il dolore di usare gli operatori *
e ++
allo stesso tempo.)
La domanda ora è cosa fai con questo? Il tipo di dati a portata di mano non è in realtà un grande abstract robusto a String
cui è facile pensare, ma invece a malapena più di un array di byte di archiviazione. Mancando di un robusto tipo di dati, è impossibile scrivere una funzione che farà lo stesso della chomp
funzione di PHperytonby . Cosa sarebbe una tale funzione in C?
do { q--; } ...
di sapere *q != 0
.
Utilizzare una libreria di stringhe , ad esempio:
Ustr *s1 = USTR1(\7, " 12345 ");
ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));
... come dici tu questo è un problema "comune", sì, devi includere un #include o giù di lì e non è incluso in libc ma non andare a inventare il tuo lavoro di hacking memorizzando puntatori casuali e size_t in quel modo porta solo a buffer overflow.
Se stai usando glib
, puoi usare g_strstrip
Solo per continuare a crescere, un'altra opzione con una stringa modificabile:
void trimString(char *string)
{
size_t i = 0, j = strlen(string);
while (j > 0 && isspace((unsigned char)string[j - 1])) string[--j] = '\0';
while (isspace((unsigned char)string[i])) i++;
if (i > 0) memmove(string, string + i, j - i + 1);
}
strlen()
restituisce un valore size_t
che può superare l'intervallo di int
. lo spazio bianco non è limitato al carattere spazio. Infine, ma soprattutto: comportamento indefinito strcpy(string, string + i * sizeof(char));
perché le matrici di origine e destinazione si sovrappongono. Usa memmove()
invece di strcpy()
.
while (isspace((int)string[i])) string[i--] = '\0';
potrebbe andare oltre l'inizio della stringa. Dovresti combinare questo ciclo con le righe precedenti e seguenti e scriverewhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
non puntava al byte null finale e il end = ++i;
problema persiste per le stringhe contenenti tutti i caratteri degli spazi bianchi. Ho appena corretto il codice.
So che ci sono molte risposte, ma inserisco la mia risposta qui per vedere se la mia soluzione è abbastanza buona.
// Trims leading whitespace chars in left `str`, then copy at almost `n - 1` chars
// into the `out` buffer in which copying might stop when the first '\0' occurs,
// and finally append '\0' to the position of the last non-trailing whitespace char.
// Reture the length the trimed string which '\0' is not count in like strlen().
size_t trim(char *out, size_t n, const char *str)
{
// do nothing
if(n == 0) return 0;
// ptr stop at the first non-leading space char
while(isspace(*str)) str++;
if(*str == '\0') {
out[0] = '\0';
return 0;
}
size_t i = 0;
// copy char to out until '\0' or i == n - 1
for(i = 0; i < n - 1 && *str != '\0'; i++){
out[i] = *str++;
}
// deal with the trailing space
while(isspace(out[--i]));
out[++i] = '\0';
return i;
}
isspace(*str)
UB quando *str < 0
.
size_t n
è buono, tuttavia l'interfaccia non informa il chiamante in alcun modo quando n
è troppo piccolo per una stringa tagliata completa. Consideratrim(out, 12, "delete data not")
Il modo più semplice per saltare gli spazi iniziali in una stringa è, imho,
#include <stdio.h>
int main()
{
char *foo=" teststring ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
return 0;
}
" foo bar "
.
Ok, questa è la mia opinione sulla domanda. Credo che sia la soluzione più concisa che modifica la stringa in atto ( free
funzionerà) ed evita qualsiasi UB. Per stringhe di piccole dimensioni, è probabilmente più veloce di una soluzione che coinvolge memmove.
void stripWS_LT(char *str)
{
char *a = str, *b = str;
while (isspace((unsigned char)*a)) a++;
while (*b = *a++) b++;
while (b > str && isspace((unsigned char)*--b)) *b = 0;
}
b > str
test è necessario solo una volta. *b = 0;
necessario solo una volta.
#include <ctype.h>
#include <string.h>
char *trim_space(char *in)
{
char *out = NULL;
int len;
if (in) {
len = strlen(in);
while(len && isspace(in[len - 1])) --len;
while(len && *in && isspace(*in)) ++in, --len;
if (len) {
out = strndup(in, len);
}
}
return out;
}
isspace
aiuta a tagliare tutti gli spazi bianchi.
strndup
per creare un nuovo buffer di stringhe escludendo gli spazi.strndup()
non fa parte dello standard C ma solo Posix. Ma poiché è abbastanza facile da implementare non è un grosso problema.
trim_space("")
ritorna NULL
. Mi aspetto un puntatore a ""
. int len;
dovrebbe essere size_t len;
. isspace(in[len - 1])
UB quando in[len - 1] < 0
.
while (isspace((unsigned char) *in) in++;
prima len = strlen(in);
sarebbe più efficiente della successivawhile(len && *in && isspace(*in)) ++in, --len;
Personalmente, mi piacerebbe il mio. Puoi usare strtok, ma devi prenderti cura di farlo (soprattutto se stai rimuovendo i personaggi principali) per sapere quale memoria è cosa.
Sbarazzarsi degli spazi finali è facile e abbastanza sicuro, poiché puoi semplicemente mettere uno 0 sopra la parte superiore dell'ultimo spazio, contando alla fine. Liberarsi degli spazi guida significa spostare le cose. Se vuoi farlo sul posto (probabilmente ragionevole) puoi semplicemente spostare tutto indietro di un personaggio fino a quando non c'è spazio iniziale. Oppure, per essere più efficiente, potresti trovare l'indice del primo carattere non spaziale e spostare tutto indietro di quel numero. Oppure, puoi semplicemente usare un puntatore al primo carattere non spaziale (ma poi devi stare attento allo stesso modo che fai con strtok).
#include "stdafx.h"
#include "malloc.h"
#include "string.h"
int main(int argc, char* argv[])
{
char *ptr = (char*)malloc(sizeof(char)*30);
strcpy(ptr," Hel lo wo rl d G eo rocks!!! by shahil sucks b i g tim e");
int i = 0, j = 0;
while(ptr[j]!='\0')
{
if(ptr[j] == ' ' )
{
j++;
ptr[i] = ptr[j];
}
else
{
i++;
j++;
ptr[i] = ptr[j];
}
}
printf("\noutput-%s\n",ptr);
return 0;
}
Un po 'tardi al gioco, ma getterò le mie routine nella mischia. Probabilmente non sono i più efficienti in assoluto, ma credo che siano corretti e semplici ( rtrim()
spingendo l'inviluppo della complessità):
#include <ctype.h>
#include <string.h>
/*
Public domain implementations of in-place string trim functions
Michael Burr
michael.burr@nth-element.com
2010
*/
char* ltrim(char* s)
{
char* newstart = s;
while (isspace( *newstart)) {
++newstart;
}
// newstart points to first non-whitespace char (which might be '\0')
memmove( s, newstart, strlen( newstart) + 1); // don't forget to move the '\0' terminator
return s;
}
char* rtrim( char* s)
{
char* end = s + strlen( s);
// find the last non-whitespace character
while ((end != s) && isspace( *(end-1))) {
--end;
}
// at this point either (end == s) and s is either empty or all whitespace
// so it needs to be made empty, or
// end points just past the last non-whitespace character (it might point
// at the '\0' terminator, in which case there's no problem writing
// another there).
*end = '\0';
return s;
}
char* trim( char* s)
{
return rtrim( ltrim( s));
}
char
argomento per isspace()
per (unsigned char)
evitare un comportamento indefinito su valori potenzialmente negativi. Evitare anche di spostare la stringa se dentro ltrim()
se non necessario.
La maggior parte delle risposte finora effettua una delle seguenti operazioni:
strlen()
prima, facendo un secondo passaggio attraverso l'intera stringa.Questa versione effettua un solo passaggio e non esegue il backtrack. Quindi potrebbe funzionare meglio degli altri, anche se solo se è comune avere centinaia di spazi finali (il che non è insolito quando si tratta dell'output di una query SQL).
static char const WHITESPACE[] = " \t\n\r";
static void get_trim_bounds(char const *s,
char const **firstWord,
char const **trailingSpace)
{
char const *lastWord;
*firstWord = lastWord = s + strspn(s, WHITESPACE);
do
{
*trailingSpace = lastWord + strcspn(lastWord, WHITESPACE);
lastWord = *trailingSpace + strspn(*trailingSpace, WHITESPACE);
}
while (*lastWord != '\0');
}
char *copy_trim(char const *s)
{
char const *firstWord, *trailingSpace;
char *result;
size_t newLength;
get_trim_bounds(s, &firstWord, &trailingSpace);
newLength = trailingSpace - firstWord;
result = malloc(newLength + 1);
memcpy(result, firstWord, newLength);
result[newLength] = '\0';
return result;
}
void inplace_trim(char *s)
{
char const *firstWord, *trailingSpace;
size_t newLength;
get_trim_bounds(s, &firstWord, &trailingSpace);
newLength = trailingSpace - firstWord;
memmove(s, firstWord, newLength);
s[newLength] = '\0';
}
strspn()
e strcspn()
in loop. Questo è molto inefficiente e il sovraccarico ridurrà il vantaggio non dimostrato del singolo passaggio in avanti. strlen()
di solito viene espanso in linea con un codice molto efficiente, non una vera preoccupazione. Tagliare l'inizio e la fine della stringa sarà molto più veloce che testare ogni carattere nella stringa per il bianco anche nel caso speciale di stringhe con pochissimi caratteri o non bianchi.
Questa è l'implementazione più breve che mi viene in mente:
static const char *WhiteSpace=" \n\r\t";
char* trim(char *t)
{
char *e=t+(t!=NULL?strlen(t):0); // *e initially points to end of string
if (t==NULL) return;
do --e; while (strchr(WhiteSpace, *e) && e>=t); // Find last char that is not \r\n\t
*(++e)=0; // Null-terminate
e=t+strspn (t,WhiteSpace); // Find first char that is not \t
return e>t?memmove(t,e,strlen(e)+1):t; // memmove string contents and terminator
}
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; }
Queste funzioni modificheranno il buffer originale, quindi se allocato dinamicamente, il puntatore originale può essere liberato.
#include <string.h>
void rstrip(char *string)
{
int l;
if (!string)
return;
l = strlen(string) - 1;
while (isspace(string[l]) && l >= 0)
string[l--] = 0;
}
void lstrip(char *string)
{
int i, l;
if (!string)
return;
l = strlen(string);
while (isspace(string[(i = 0)]))
while(i++ < l)
string[i-1] = string[i];
}
void strip(char *string)
{
lstrip(string);
rstrip(string);
}
rstrip()
invoca un comportamento indefinito sulla stringa vuota. lstrip()
è inutilmente lento sulla stringa con una lunga porzione iniziale di caratteri bianchi. isspace()
non dovrebbe essere passato un char
argomento perché invoca un comportamento indefinito su valori negativi diversi da EOF
.
Cosa ne pensi dell'utilizzo della funzione StrTrim definita nell'intestazione Shlwapi.h.? È semplice piuttosto che definire da solo.
I dettagli sono disponibili su:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Se hai
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
Questo darà ausCaptain
come "GeorgeBailey"
no "GeorgeBailey "
.
Per tagliare le corde da entrambi i lati uso il vecchio ma il cattivo;) Può tagliare qualsiasi cosa con ASCII meno di uno spazio, il che significa che anche i caratteri di controllo saranno tagliati!
char *trimAll(char *strData)
{
unsigned int L = strlen(strData);
if(L > 0){ L--; }else{ return strData; }
size_t S = 0, E = L;
while((!(strData[S] > ' ') || !(strData[E] > ' ')) && (S >= 0) && (S <= L) && (E >= 0) && (E <= L))
{
if(strData[S] <= ' '){ S++; }
if(strData[E] <= ' '){ E--; }
}
if(S == 0 && E == L){ return strData; } // Nothing to be done
if((S >= 0) && (S <= L) && (E >= 0) && (E <= L)){
L = E - S + 1;
memmove(strData,&strData[S],L); strData[L] = '\0';
}else{ strData[0] = '\0'; }
return strData;
}
size_t
invece di unsigned int
. Il codice ha molti test ridondanti e richiama comportamenti indefiniti strncpy(strData,&strData[S],L)
perché gli array di origine e destinazione si sovrappongono. Usa memmove()
invece di strncpy()
.
Includo solo il codice perché il codice pubblicato finora sembra non ottimale (e non ho ancora il rappresentante per commentare).
void inplace_trim(char* s)
{
int start, end = strlen(s);
for (start = 0; isspace(s[start]); ++start) {}
if (s[start]) {
while (end > 0 && isspace(s[end-1]))
--end;
memmove(s, &s[start], end - start);
}
s[end - start] = '\0';
}
char* copy_trim(const char* s)
{
int start, end;
for (start = 0; isspace(s[start]); ++start) {}
for (end = strlen(s); end > 0 && isspace(s[end-1]); --end) {}
return strndup(s + start, end - start);
}
strndup()
è un'estensione GNU. Se non lo hai o qualcosa di equivalente, fai il tuo. Per esempio:
r = strdup(s + start);
r[end-start] = '\0';
isspace(0)
è definito come falso, è possibile semplificare entrambe le funzioni. Sposta anche l' memmove()
interno del if
blocco.
Qui utilizzo l'allocazione dinamica della memoria per tagliare la stringa di input alla funzione trimStr. Innanzitutto, troviamo quanti caratteri non vuoti esistono nella stringa di input. Quindi, assegniamo un array di caratteri con quella dimensione e ci occupiamo del carattere nullo. Quando utilizziamo questa funzione, dobbiamo liberare la memoria all'interno della funzione principale.
#include<stdio.h>
#include<stdlib.h>
char *trimStr(char *str){
char *tmp = str;
printf("input string %s\n",str);
int nc = 0;
while(*tmp!='\0'){
if (*tmp != ' '){
nc++;
}
tmp++;
}
printf("total nonempty characters are %d\n",nc);
char *trim = NULL;
trim = malloc(sizeof(char)*(nc+1));
if (trim == NULL) return NULL;
tmp = str;
int ne = 0;
while(*tmp!='\0'){
if (*tmp != ' '){
trim[ne] = *tmp;
ne++;
}
tmp++;
}
trim[nc] = '\0';
printf("trimmed string is %s\n",trim);
return trim;
}
int main(void){
char str[] = " s ta ck ove r fl o w ";
char *trim = trimStr(str);
if (trim != NULL )free(trim);
return 0;
}
Ecco come lo faccio. Taglia la stringa in posizione, quindi non preoccuparti di deallocare una stringa restituita o perdere il puntatore su una stringa allocata. Potrebbe non essere la risposta più breve possibile, ma dovrebbe essere chiaro alla maggior parte dei lettori.
#include <ctype.h>
#include <string.h>
void trim_str(char *s)
{
const size_t s_len = strlen(s);
int i;
for (i = 0; i < s_len; i++)
{
if (!isspace( (unsigned char) s[i] )) break;
}
if (i == s_len)
{
// s is an empty string or contains only space characters
s[0] = '\0';
}
else
{
// s contains non-space characters
const char *non_space_beginning = s + i;
char *non_space_ending = s + s_len - 1;
while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;
size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;
if (s != non_space_beginning)
{
// Non-space characters exist in the beginning of s
memmove(s, non_space_beginning, trimmed_s_len);
}
s[trimmed_s_len] = '\0';
}
}
char* strtrim(char* const str)
{
if (str != nullptr)
{
char const* begin{ str };
while (std::isspace(*begin))
{
++begin;
}
auto end{ begin };
auto scout{ begin };
while (*scout != '\0')
{
if (!std::isspace(*scout++))
{
end = scout;
}
}
auto /* std::ptrdiff_t */ const length{ end - begin };
if (begin != str)
{
std::memmove(str, begin, length);
}
str[length] = '\0';
}
return str;
}