web-dev-qa-db-fra.com

Comment est-ce que je coupe les espaces de début/fin de manière standard?

Existe-t-il une méthode propre, de préférence standard, pour couper les espaces de début et de fin d'une chaîne en C? Je me débrouillerais, mais je penserais que c'est un problème commun avec une solution tout aussi commune.

156
coledot

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;
}
148
Adam Rosenfield

En voici un qui déplace la chaîne dans la première position de votre tampon. Vous voudrez peut-être ce comportement afin que, si vous avez alloué dynamiquement la chaîne, vous pouvez toujours la libérer sur le même pointeur que celui renvoyé par 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;
}

Test d'exactitude:

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

Le fichier source était trim.c. Compilé avec 'cc trim.c -o trim'.

31
indiv

Ma solution La corde doit être changeante. L’avantage, au-dessus de certaines des autres solutions, de déplacer la partie non-espace au début afin que vous puissiez continuer à utiliser l’ancien pointeur, au cas où vous deviez le libérer () ultérieurement.

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

Cette version crée une copie de la chaîne avec strndup () au lieu de la modifier sur place. strndup () nécessite _GNU_SOURCE, alors vous devez peut-être créer votre propre strndup () avec malloc () et 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);
}
20
jkramer

Voici ma bibliothèque C mini pour rogner à gauche, à droite, les deux, tous à la place et à la place, et pour rogner un ensemble de caractères spécifiés (ou un espace blanc par défaut).

contenu de 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

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

La routine principale fait tout. Il coupe sur place si src == dst, sinon, Fonctionne comme les routines strcpy . Il coupe un jeu de caractères spécifié dans la chaîne delim, ou un espace si null .. ... Il coupe la gauche, la droite, les deux, et tous (comme tr) . Il n'y a pas grand chose à faire, et itère une seule fois sur la chaîne. Certaines personnes pourraient se plaindre que le trim droit 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 aller jusqu'au bout de la chaîne pour des découpes correctes, afin que vous fassiez le travail à votre guise.) Il peut y avoir des arguments à propos de la gestion du pipeline et de la taille du cache, entre autres: . Comme la solution fonctionne de gauche à droite et ne s'exécute qu'une seule fois, elle peut également être étendue aux flux. Limitations: cela ne fonctionne pas pas fonctionne sur unicode strings.

9
Shoots the Moon

Voici ma tentative d'une fonction de découpage sur place simple mais correcte.

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

En retard à la fête

Caractéristiques:
1. Coupez le début rapidement, comme dans plusieurs autres réponses.
2. Après être allé jusqu'au bout, coupez à droite avec un seul test par boucle. Comme @ jfm3, mais fonctionne pour une chaîne entièrement blanche)
3. Pour éviter un comportement non défini lorsque char est une signature char, transformez *s en unsigned char

Character processing "Dans tous les cas, l'argument est une int, dont la valeur doit pouvoir être représentée en tant que unsigned char ou doit être égale à la valeur de la macro EOF. Si l'argument a une autre valeur, le comportement est indéfini." 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 desire to shift the trimmed string

  return s;
}

@chqrlie commenté ce qui précède ne décale pas la chaîne coupée. Faire cela....

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

Un autre, avec une ligne faisant le vrai travail:

#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);
}
4
Daniel

Je n’ai pas aimé la plupart de ces réponses parce qu’elles ont fait une ou plusieurs des choses suivantes ...

  1. Renvoyé un pointeur différent à l'intérieur de la chaîne du pointeur d'origine (genre de douleur pour jongler avec deux pointeurs différents à la même chose).
  2. Utilisation gratuite de choses comme strlen () qui pré-itèrent la chaîne entière.
  3. Utilisation de fonctions de bibliothèque non portables spécifiques au système d'exploitation.
  4. Backscanné.
  5. Comparaison utilisée avec '' au lieu de isspace () afin que TAB/CR/LF soient préservés.
  6. Mémoire gaspillée avec de grands tampons statiques.
  7. Cycles perdus avec des fonctions coûteuses telles que sscanf/sprintf .

Voici ma version:

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';
}
3
Jason Stewart

Voici une solution semblable à la routine de modification sur place @ adam-rosenfields mais sans recourir inutilement à strlen (). Comme @jkramer, la chaîne est ajustée à gauche dans la mémoire tampon afin que vous puissiez libérer le même pointeur. Pas optimal pour les grandes chaînes car il n'utilise pas memmove. Inclut les opérateurs ++/- mentionnés par @ jfm3. Tests unitaires basés sur FCTX inclus.

#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();
3
Rhys Ulerich

Très tard à la fête ...

Solution de numérisation directe à passage unique sans retour en arrière. Chaque caractère de la chaîne source est testé exactement une fois que deux fois. (Cela devrait donc être plus rapide que la plupart des autres solutions ici, en particulier si la chaîne source contient beaucoup d'espaces de fin.)

Cela inclut deux solutions, l’une pour copier et couper une chaîne source dans une autre chaîne de destination et l’autre pour couper la chaîne source en place. Les deux fonctions utilisent le même code.

La chaîne (modifiable) étant déplacée sur place, le pointeur d'origine sur celle-ci reste inchangé.

#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);
}
2
David R Tribble

La meilleure façon de sauter les espaces de début dans une chaîne est

#include <stdio.h>

int main()
{
char *foo="     teststring      ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
    return 0;
}
2
Zibri

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 les UB Pour les petites chaînes, c'est probablement plus rapide qu'une solution impliquant 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;
}
1
poby

Je ne suis pas sûr de ce que vous considérez comme "indolore".

Les cordes Do sont assez douloureuses. Nous pouvons trouver trivialement la première position de caractère non-blanc:

 while (isspace (* p)) p ++; 

Nous pouvons trouver la dernière position de caractère non-blanc avec deux mouvements triviaux similaires:

 while (* q) q ++; 
 do {q--; } while (isspace (* q)); 

(Je vous ai épargné la peine d'utiliser simultanément les opérateurs * et ++.)

La question est maintenant que faites-vous avec ceci? Le type de données en question n'est pas vraiment un gros résumé String robuste, facile à prendre en compte, mais plutôt un simple tableau de octets de stockage. En l'absence d'un type de données robuste, il est impossible d'écrire une fonction qui fera la même chose que la fonction chomp de PHperytonby. Que reviendrait une telle fonction en C?

1
jfm3

Juste pour garder cette croissance, encore une option avec une chaîne modifiable:

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);
}
1
wallek876
#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;
}
1
Balkrishna Talele
#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 aide à réduire tous les espaces blancs.

  • Exécutez une première boucle pour vérifier à partir du dernier octet le caractère d'espace et réduisez la variable de longueur
  • Exécutez une seconde boucle pour vérifier à partir du premier octet le caractère d'espace, réduisez la variable de longueur et incrémentez le pointeur de caractère.
  • Enfin, si la longueur de la variable est supérieure à 0, utilisez strndup pour créer un nouveau tampon de chaîne en excluant les espaces.
1
rashok

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. 

// 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;
}
1
Ekeyme Mo

s a été extrêmement utile, je voulais dire que je suis heureux que cet article soit disponible et montrer ce que j'ai pu faire avec les exemples. J'avais besoin de marquer une chaîne plus grande, puis de prendre la (les) sous-chaîne (s) et de trouver la dernière - afin de pouvoir supprimer une nouvelle ligne de l'appel de fgets (), ainsi que de supprimer les espaces au début de ce jeton - afin de pouvoir facilement comparer avec une chaîne statique. Le premier exemple dans l'article ci-dessus m'a amené là-bas, alors merci. Voici comment j'ai utilisé les exemples de code et la sortie que j'ai obtenue.

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;

}

Sortie

Le fichier existe.

String: État DBS: Démarrage DBS - Initialisation de la configuration DBS

Etat DBS

DBS Startup

DBS Startup

Initialisation de la configuration DBS

Initialisation de la configuration DBS

Les cordes sont égales.

Tokeniser Fait.

1
Diana

Si vous utilisez glib, vous pouvez utiliser g_strstrip

1
sleepycal

Utilisez une bibliothèque string , par exemple:

Ustr *s1 = USTR1(\7, " 12345 ");

ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));

... comme vous dites que c'est un problème "commun", oui, vous devez inclure un #include ou autre et il n'est pas inclus dans la libc, mais n'inventez pas votre propre travail de piratage en stockant des pointeurs aléatoires et size_t qui ne conduit qu'à buffer déborde.

1
James Antill

Jusqu'à présent, la plupart des réponses ont eu l'une des conséquences suivantes:

  1. Revenir en arrière à la fin de la chaîne (c'est-à-dire trouver la fin de la chaîne, puis rechercher en arrière jusqu'à ce qu'un caractère autre que l'espace soit trouvé) ou
  2. Appelez d'abord strlen(), en faisant un deuxième passage à travers toute la chaîne.

Cette version ne fait qu'un passage et ne fait pas retour en arrière. Par conséquent, il peut être plus performant que les autres, mais uniquement s’il est courant d’avoir des centaines d’espaces de fin (ce qui n’est pas inhabituel pour la sortie d’une requête 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';
}
0
finnw

Ces fonctions modifieront le tampon d'origine. Ainsi, s'il est alloué dynamiquement, le pointeur Original peut être libéré.

#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);
}
0
Telc

Voici comment je le fais. Il limite la chaîne en place, vous n'avez donc pas à craindre de désallouer une chaîne renvoyée ou de perdre le pointeur sur une chaîne allouée. Ce n'est peut-être pas la réponse la plus courte possible, mais cela devrait être clair pour la plupart des lecteurs.

#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';
    }
}
0
Isaac To

Pour couper mes ficelles des deux côtés, j'utilise le vieux mais le vaurien;) Il peut couper n'importe quoi avec moins de place qu'un espace, ce qui signifie que les caractères de contrôle seront aussi coupés!

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;
}
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;
}
0
Mitch Laber

Ici, j'utilise l'allocation de mémoire dynamique pour ajuster 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 de cette taille en prenant soin du caractère terminé par un caractère nul. Lorsque nous utilisons cette fonction, nous devons libérer la mémoire à l’intérieur de la fonction 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;
}
0
saeed_falahat

J'inclus seulement le code parce que le code posté jusqu'à présent semble sous-optimal (et je n'ai pas le représentant à commenter.)

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() est une extension GNU. Si vous ne l'avez pas ou quelque chose d'équivalent, lancez le vôtre. Par exemple:

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

Personnellement, je roulerais le mien. Vous pouvez utiliser strtok, mais vous devez faire attention à ce que vous sachiez ce que la mémoire contient (surtout si vous enlevez les caractères principaux).

Il est facile de se débarrasser des espaces de fuite et de le sécuriser car vous pouvez simplement mettre un 0 dans le dernier espace, en comptant à partir de la fin. Se débarrasser des espaces de repère signifie déplacer des choses. Si vous voulez le faire à la place (probablement raisonnable), vous pouvez simplement continuer à tout décaler d’un personnage jusqu’à ce qu’il n’y ait plus de place. 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 sur le premier caractère non espace (mais vous devez ensuite faire attention de la même manière qu'avec strtok).

0
Ben

Un peu tard pour le jeu, mais je vais jeter mes routines dans la mêlée. Ils ne sont probablement pas l'efficacité la plus absolue, mais je crois qu'ils sont corrects et simples (avec rtrim() repoussant les limites de la complexité):

#include <ctype.h>
#include <string.h>

/*
    Public domain implementations of in-place string trim functions

    Michael Burr
    [email protected]
    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));
}
0
Michael Burr

C’est la mise en oeuvre la plus courte possible à laquelle je puisse penser:

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
}
0
Michał Gawlas

Que pensez-vous de l'utilisation de la fonction StrTrim définie dans l'en-tête Shlwapi.h.? C'est assez simple, définissant 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 tu as
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
Ceci donnera ausCaptain comme "GeorgeBailey" pas "GeorgeBailey ".

0
Carthi