Pregunta

¿Existe un método limpio, preferiblemente estándar, para recortar los espacios en blanco iniciales y finales de una cadena en C?Yo haría el mío, pero creo que se trata de un problema común con una solución igualmente común.

¿Fue útil?

Solución

Si puedes modificar la cadena:

// 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 no puede modificar la cadena, puede utilizar básicamente el mismo método:

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

Otros consejos

Aquí hay uno que desplaza la cadena a la primera posición de su búfer.Es posible que desees este comportamiento para que, si asignaste dinámicamente la cadena, aún puedas liberarla en el mismo puntero que devuelve 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;
}

Prueba de corrección:

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

El archivo fuente era trim.c.Compilado con 'cc trim.c -o trim'.

Mi solución.La cadena debe poder cambiarse.La ventaja sobre algunas de las otras soluciones es que mueve la parte no espacial al principio para que puedas seguir usando el puntero anterior, en caso de que tengas que liberarlo() más adelante.

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

Esta versión crea una copia de la cadena con strndup() en lugar de editarla en su lugar.strndup() requiere _GNU_SOURCE, por lo que tal vez necesites crear tu propio strndup() con malloc() y 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);
}

Aquí está mi mini biblioteca C para recortar hacia la izquierda, hacia la derecha, ambos, todos, en su lugar y por separado, y recortar un conjunto de caracteres específicos (o espacios en blanco de forma predeterminada).

Contenido 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

Contenido 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 única rutina principal lo hace todo.Se recorta en su lugar si src == horario de verano, de lo contrario, funciona como el strcpy rutinas.Recorta un conjunto de caracteres especificados en la cadena. delim, o espacio en blanco si es nulo.Recorta hacia la izquierda, hacia la derecha, ambos y todos (como tr).No hay mucho que hacer y itera sobre la cadena solo una vez.Algunas personas pueden quejarse de que recortar a la derecha comienza por la izquierda; sin embargo, no se necesita strlen que comience por la izquierda de todos modos.(De una forma u otra, debe llegar al final de la cadena para realizar los ajustes correctos, por lo que también puede hacer el trabajo sobre la marcha). Es posible que haya argumentos sobre la canalización y los tamaños de caché y demás, quién sabe. .Dado que la solución funciona de izquierda a derecha y se itera solo una vez, se puede ampliar para que funcione también en secuencias.Limitaciones:lo hace no trabajar en Unicode instrumentos de cuerda.

Aquí está mi intento de una función de recorte in situ simple pero correcta.

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

Tarde a la fiesta de recortes

Características:
1.Recorte el comienzo rápidamente, como en otras respuestas.
2.Después de llegar al final, recortar a la derecha con sólo 1 prueba por bucle.Como @jfm3, pero funciona para una cadena de espacios en blanco)
3.Para evitar un comportamiento indefinido cuando char es un firmado char, elenco *s a unsigned char.

Manejo de personajes "En todos los casos el argumento es una int, cuyo valor será representable como un unsigned char o será igual al valor de la macro EOF.Si el argumento tiene cualquier otro valor, el comportamiento no está definido." 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 comentado lo anterior no desplaza la cuerda recortada.Para hacerlo....

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

Aquí hay una solución similar a la rutina de modificación in situ de @adam-rosenfields pero sin recurrir innecesariamente a strlen().Al igual que @jkramer, la cadena se ajusta a la izquierda dentro del búfer para que puedas liberar el mismo puntero.No es óptimo para cadenas grandes ya que no utiliza memmove.Incluye los operadores ++/-- que menciona @jfm3. FCTX-Pruebas unitarias basadas en incluidas.

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

Otro, con una línea que hace el verdadero trabajo:

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

No me gustaron la mayoría de estas respuestas porque hicieron una o más de las siguientes cosas...

  1. Devolvió un puntero diferente dentro de la cadena del puntero original (es una especie de molestia hacer malabarismos con dos punteros diferentes para lo mismo).
  2. Hizo uso gratuito de cosas como strlen() que iteran previamente toda la cadena.
  3. Se utilizaron funciones de biblioteca no portátiles específicas del sistema operativo.
  4. Escaneado hacia atrás.
  5. Comparación usada con ' ' en lugar de isespacio() para que se conserven TAB/CR/LF.
  6. Memoria desperdiciada con grandes buffers estáticos.
  7. Ciclos desperdiciados con funciones de alto costo como sscanf/sprintf.

Aquí está mi versión:

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

Muy tarde a la fiesta...

Solución de escaneo hacia adelante de un solo paso sin retroceso.Cada carácter de la cadena fuente se prueba exactamente una vez dos veces.(Por lo tanto, debería ser más rápido que la mayoría de las otras soluciones aquí, especialmente si la cadena de origen tiene muchos espacios finales).

Esto incluye dos soluciones, una para copiar y recortar una cadena de origen en otra cadena de destino y la otra para recortar la cadena de origen en su lugar.Ambas funciones utilizan el mismo código.

La cadena (modificable) se mueve en su lugar, por lo que el puntero original permanece sin cambios.

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

No estoy seguro de qué considera "indoloro".

Las cuerdas C son bastante dolorosas.Podemos encontrar la posición del primer carácter que no sea un espacio en blanco de manera trivial:

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

Podemos encontrar la última posición del carácter que no es un espacio en blanco con dos movimientos triviales similares:

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

(Te he ahorrado el dolor de usar el * y ++ operadores al mismo tiempo.)

La pregunta ahora es ¿qué haces con esto?El tipo de datos en cuestión no es realmente un gran resumen sólido. String Es fácil pensar en eso, pero en realidad apenas es más que una serie de bytes de almacenamiento.Al carecer de un tipo de datos robusto, es imposible escribir una función que haga lo mismo que la de PHPerytonby. chomp función.¿Qué devolvería tal función en C?

Usar una biblioteca de cadenas, por ejemplo:

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

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

... como usted dice, este es un problema "común", sí, necesita incluir un #include más o menos y no está incluido en libc, pero no invente su propio trabajo de pirateo almacenando punteros aleatorios y size_t de esa manera solo conduce a desbordamientos del buffer.

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

Esto fue extremadamente útil, quería decir que me alegraba que esta publicación estuviera disponible y mostrar lo que pude hacer con los ejemplos.Necesitaba tokenizar una cadena más grande, y luego tomar las subcadenas y encontrar la última, para poder eliminar una nueva línea de la llamada fgets() y también eliminar el espacio en blanco del frente de ese token, para poder compárelo fácilmente con una cadena estática.El primer ejemplo de la publicación anterior me llevó allí, así que gracias.Así es como utilicé los ejemplos de código y el resultado que obtuve.

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;

}

Producción

El archivo existe.

Cadena:Estado DBS:Inicio de DBS: inicialización de la configuración de DBS

Estado DBS

Inicio de DBS

Inicio de DBS

Inicializando la configuración de DBS

Inicializando la configuración de DBS

Las cuerdas son iguales.

Tokenización realizada.

Si estas usando glib, entonces puedes usar g_strstrip

Sólo para que esto siga creciendo, una opción más con una cadena modificable:

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

Sé que hay muchas respuestas, pero publico mi respuesta aquí para ver si mi solución es lo suficientemente buena.

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

La forma más sencilla de omitir los espacios iniciales en una cadena es, en mi humilde opinión,

#include <stdio.h>

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

Ok, esta es mi opinión sobre la pregunta.Creo que es la solución más concisa que modifica la cadena en su lugar (free funcionará) y evita cualquier UB.Para cadenas pequeñas, probablemente sea más rápido que una solución que incluya 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 ayuda a recortar todos los espacios en blanco.

  • Ejecute un primer bucle para verificar desde el último byte el carácter de espacio y reducir la variable de longitud.
  • Ejecute un segundo bucle para verificar desde el primer byte el carácter de espacio y reduzca la variable de longitud e incremente el puntero de carácter.
  • Finalmente, si la variable de longitud es mayor que 0, entonces use strndup para crear un nuevo búfer de cadena excluyendo espacios.

Personalmente, haría el mío.Puede usar strtok, pero debe tener cuidado al hacerlo (especialmente si está eliminando personajes principales) y saber qué es qué memoria.

Deshacerse de los espacios finales es fácil y bastante seguro, ya que puedes simplemente poner un 0 encima del último espacio, contando hacia atrás desde el final.Deshacerse de los espacios principales significa mover las cosas.Si desea hacerlo en su lugar (probablemente sea sensato), puede seguir moviendo todo hacia atrás un carácter hasta que no haya espacio inicial.O, para ser más eficiente, puede encontrar el índice del primer carácter que no sea un espacio y desplazar todo hacia atrás en ese número.O simplemente podrías usar un puntero al primer carácter que no sea un espacio (pero luego debes tener cuidado de la misma manera que lo haces con strtok).

Un poco tarde para el juego, pero lanzaré mis rutinas a la palestra.Probablemente no sean los más absolutamente eficientes, pero creo que son correctos y simples (con rtrim() empujando los límites de la complejidad):

#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 mayoría de las respuestas hasta ahora hacen una de las siguientes:

  1. Retroceda al final de la cadena (es decir,encontrar el final de la cadena y luego buscar hacia atrás hasta encontrar un carácter que no sea un espacio) o
  2. Llamar strlen() primero, haciendo una segunda pasada por toda la cuerda.

Esta versión hace una sola pasada y no retrocede.Por lo tanto, puede funcionar mejor que los demás, aunque sólo si es común tener cientos de espacios finales (lo cual no es inusual cuando se trata del resultado de una consulta 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';
}

Esta es la implementación más corta posible que se me ocurre:

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
}

Estas funciones modificarán el búfer original, por lo que si se asigna dinámicamente, el puntero original puede liberarse.

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

¿Qué opinas sobre el uso de la función StrTrim definida en el encabezado Shlwapi.h.?Es sencillo y más bien definitorio por tu cuenta.
Los detalles se pueden encontrar en:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

Si usted tiene
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
esto le dará ausCaptain como "GeorgeBailey" no "GeorgeBailey ".

Para recortar mis cuerdas de ambos lados, uso el viejo pero el Gooody;) puede recortar cualquier cosa con ASCII menos que un espacio, lo que significa que los chars de control también se recortarán.

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

Solo incluyo código porque el código publicado hasta ahora parece subóptimo (y todavía no tengo el representante para comentar).

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() es una extensión GNU.Si no lo tienes o algo equivalente, haz el tuyo propio.Por ejemplo:

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

Aquí utilizo la asignación de memoria dinámica para recortar la cadena de entrada a la función trimStr.Primero, encontramos cuántos caracteres no vacíos existen en la cadena de entrada.Luego, asignamos una matriz de caracteres con ese tamaño y nos ocupamos del carácter terminado en nulo.Cuando usamos esta función, necesitamos liberar la memoria dentro de la función principal.

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

Así es como lo hago.Recorta la cadena en su lugar, por lo que no se preocupa por desasignar una cadena devuelta o perder el puntero a una cadena asignada.Puede que no sea la respuesta más corta posible, pero debería quedar clara para la mayoría de los lectores.

#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';
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top