Domanda

Esiste un metodo pulito, preferibilmente standard, per tagliare gli spazi bianchi iniziali e finali da una stringa in C? Mi piacerebbe il mio, ma penso che questo sia un problema comune con una soluzione altrettanto comune.

È stato utile?

Soluzione

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;
}

Altri suggerimenti

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 trim ():

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( str + len - 1 != endp )
            *(endp + 1) = '\0';
    else if( frontp != str &&  endp == frontp )
            *str = '\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:

int main(int argc, char *argv[])
{
    char *sample_strings[] =
    {
            "nothing to trim",
            "    trim the front",
            "trim the back     ",
            " trim one char front and back ",
            " trim one char front",
            "trim one char back ",
            "                   ",
            " ",
            "a",
            "",
            NULL
    };
    char test_buffer[64];
    int index;

    for( index = 0; sample_strings[index] != NULL; ++index )
    {
            strcpy( test_buffer, sample_strings[index] );
            printf("[%s] -> [%s]\n", sample_strings[index],
                                     trim(test_buffer));
    }

    /* The test prints the following:
    [nothing to trim] -> [nothing to trim]
    [    trim the front] -> [trim the front]
    [trim the back     ] -> [trim the 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]
    [] -> []
    */

    return 0;
}

Il file di origine era trim.c. Compilato con 'cc trim.c -o trim'.

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);
}

Ecco la mia mini libreria C per tagliare a sinistra, a destra, entrambi, tutti, sul posto e separati, e tagliare un set di caratteri specificati (o uno spazio bianco per impostazione predefinita).

contenuti di strlib.h:

#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

contenuti di strlib.c:

#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 aggiusta se src == dst , altrimenti, funziona come le routine strcpy . Taglia un set di caratteri specificato nella stringa delim o 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 il taglio 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 .

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.
}

In ritardo alla festa di rifinitura

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 char , eseguire il cast di * s in char senza segno .

  

Gestione dei caratteri " In tutti i casi l'argomento è un int , il cui valore deve essere rappresentabile come carattere non firmato o deve uguale al valore della macro EOF . Se l'argomento ha altri valori, il comportamento non è definito. & Quot; C11 & # 167; 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 desire to shift the trimmed string

  return s;
}

@chqrlie ha commentato che quanto sopra non sposta la stringa tagliata. Per farlo ....

// 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);
  }

  return (s == original) ? s : memove(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);
}

La maggior parte di queste risposte non mi è piaciuta perché hanno fatto una o più delle seguenti ...

  1. Restituito un puntatore diverso all'interno della stringa del puntatore originale (una specie di dolore per destreggiarsi tra due puntatori diversi nella stessa cosa).
  2. Ha fatto un uso gratuito di cose come strlen () che hanno ripetuto l'intera stringa.
  3. Utilizzate funzioni lib specifiche del sistema operativo non portatili.
  4. Backscanned.
  5. Utilizzato il confronto con '' anziché isspace () in modo da preservare TAB / CR / LF.
  6. Memoria sprecata con buffer statici di grandi dimensioni.
  7. Cicli sprecati con funzioni ad alto costo come sscanf / sprintf .

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';
}

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 di origine in un'altra stringa di destinazione e l'altra per tagliare la stringa di origine 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);
}

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 e robusto abstract String a cui è facile pensare, ma piuttosto 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 funzione chomp di PHperytonby. Cosa sarebbe una tale funzione in C?

Utilizza 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 " comune " problema, sì, è necessario includere un #include o giù di lì e non è incluso in libc ma non andare a inventare il proprio lavoro di hacking memorizzando puntatori casuali e size_t in quel modo porta solo a buffer overflow.

#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;
}

s è stato estremamente utile, volevo dire che ero contento che questo post fosse disponibile e di mostrare cosa ero in grado di fare con gli esempi. Avevo bisogno di tokenizzare una stringa più grande, quindi prendere la sottostringa (s) e trovare l'ultima - in modo da poter rimuovere una nuova riga dalla chiamata fgets (), e anche rimuovere lo spazio bianco dalla parte anteriore di quel token - così ho potuto facilmente confrontarlo con una stringa statica. Il primo esempio nel post sopra mi ha portato lì, quindi grazie. Ecco come ho usato gli esempi di codice e l'output che ho ottenuto.

int _tmain(int argc, _TCHAR* argv[])
{
   FILE * fp;   // test file
   char currDBSStatstr[100] = {"/0"};
   char *beg;
   char *end;
   char *str1;
   char str[] = "Initializing DBS Configuration";
   fp = fopen("file2-1.txt","r");
   if (fp != NULL)
   {
      printf("File exists.\n");
      fgets(currDBSStatstr, sizeof(currDBSStatstr), fp);
   }
   else
   {
      printf("Error.\n");
      exit(2);
   }  
   //print string
   printf("String: %s\n", currDBSStatstr);
   //extract first string
   str1 = strtok(currDBSStatstr, ":-");
   //print first token
   printf("%s\n", str1);
   //get more tokens in sequence
   while(1)
   {
      //extract more tokens in sequence
      str1 = strtok(NULL, ":-");
      //check to see if done
      if (str1 == NULL)
      {
         printf("Tokenizing Done.\n");
         exit(0);
      }
      //print string after tokenizing Done
      printf("%s\n", str1);
      end = str1 + strlen(str1) - 1;
      while((end > str1) && (*end == '\n'))
      {
         end--;
         *(end+1) = 0;
         beg = str1;
         while(isspace(*str1))
            str1++;
      }
      printf("%s\n", str1);
      if (strcmp(str, str1) == 0)
         printf("Strings are equal.\n");
   }
   return 0;

}

Output

  

Il file esiste.

     

Stringa: stato DBS: avvio DBS - inizializzazione configurazione DBS

     

Stato DBS

     

Avvio DBS

     

Avvio DBS

     

Inizializzazione della configurazione DBS

     

Inizializzazione della configurazione DBS

     

Le stringhe sono uguali.

     

Tokenizing Fatto.

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);
}

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;
}

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;
}

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;
}
#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.

  • Esegui un primo ciclo per controllare l'ultimo carattere dello spazio per il carattere e ridurre la variabile di lunghezza
  • Esegui un secondo ciclo per controllare dal primo byte il carattere dello spazio e ridurre la variabile di lunghezza e incrementare il puntatore a caratteri.
  • Infine, se la variabile di lunghezza è maggiore di 0, utilizzare strndup per creare un nuovo buffer di stringhe escludendo gli spazi.

Personalmente, mi piacerebbe il mio. Puoi usare strtok, ma devi prenderti cura di farlo (in particolare se rimuovi i personaggi principali) per sapere quale memoria è cosa.

Sbarazzarsi degli spazi finali è facile e abbastanza sicuro, in quanto 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 come fai con strtok).

Un po 'tardi al gioco, ma lancerò le mie routine nella mischia. Probabilmente non sono i più efficienti in assoluto, ma credo che siano corretti e siano semplici (con rtrim () che spingono 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));
}

La maggior parte delle risposte finora effettua una delle seguenti operazioni:

  1. Backtrack alla fine della stringa (ovvero trova la fine della stringa e cerca indietro fino a quando non viene trovato un carattere non spaziale) oppure
  2. Chiama prima strlen () , 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';
}

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
}

Queste funzioni modificheranno il buffer originale, quindi se allocato dinamicamente, l'originale il puntatore 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);
}

Cosa 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 / it-it / library / windows / desktop / bb773454 (v = vs.85) .aspx

Se hai
char ausCaptain [] = " GeorgeBailey " ;;
StrTrim (ausCaptain, " ");
Questo darà ausCaptain come " GeorgeBailey " non " 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 verranno 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;
}

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. Ad esempio:

r = strdup(s + start);
r[end-start] = '\0';

Qui utilizzo l'allocazione dinamica della memoria per tagliare la stringa di input con la 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';
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top