¿Existe un método limpio, preferiblemente estándar, para recortar los espacios en blanco iniciales y finales de una cadena en C? Haría el mío, pero creo que este es un problema común con una solución igualmente común.
¿Cómo recorte los espacios en blanco iniciales/posteriores de forma estándar?
coledoto
Si, y SOLO SI, solo hay un bloque de texto contiguo entre espacios en blanco, puede usar una sola llamada para strtok(3)
al igual que:
char *trimmed = strtok(input, "\r\t\n ");
Esto funciona para cadenas como las siguientes:
" +1.123.456.7890 "
" 01-01-2020\n"
"\t2.523"
Esto no funcionará para cadenas que contienen espacios en blanco entre bloques que no son espacios en blanco, como " hi there "
. Probablemente sea mejor evitar este enfoque, pero ahora está aquí en su caja de herramientas si lo necesita.
-
deberías lanzar el
char
argumento aisspace()
para(unsigned char)
para evitar un comportamiento indefinido en valores potencialmente negativos. También evite mover la cuerda si enltrim()
si no es necesario.– chqrlie
8 oct 2016 a las 13:34
chux – Reincorporar a Monica
Tarde a la fiesta de recortes
Características:
1. Recorte el comienzo rápidamente, como en varias otras respuestas.
2. Después de llegar al final, recortar el derecho con solo 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
emitir *s
para unsigned char
.
Manejo de personajes “En todos los casos el argumento es un
int
cuyo valor será representable como ununsigned char
o será igual al valor de la macroEOF
. 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 desired, shift the trimmed string
return s;
}
@chqrlie comentó que lo anterior no cambia la cadena 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); // older errant code
len = (size_t) (p - s + 1); // Thanks to @theriver
}
return (s == original) ? s : memmove(original, s, len + 1);
}
-
Yay, finalmente alguien que conoce el comportamiento indefinido de ctype.
– Roland Illig
18 de septiembre de 2016 a las 2:21
Aquí hay uno que cambia la cadena a la primera posición de su búfer. Es posible que desee este comportamiento para que, si asignó dinámicamente la cadena, aún pueda 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( frontp != str && endp == frontp )
*str="\0";
else if( str + len - 1 != endp )
*(endp + 1) = '\0';
/* Shift the string so that it starts at str so that if it's dynamically
* allocated, we can still free it on the returned pointer. Note the reuse
* of endp to mean the front of the string buffer now.
*/
endp = str;
if( frontp != str )
{
while( *frontp ) { *endp++ = *frontp++; }
*endp = '\0';
}
return str;
}
Prueba de corrección:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* Paste function from above here. */
int main()
{
/* The test prints the following:
[nothing to trim] -> [nothing to trim]
[ trim the front] -> [trim the front]
[trim the back ] -> [trim the back]
[ trim front and back ] -> [trim front and back]
[ trim one char front and back ] -> [trim one char front and back]
[ trim one char front] -> [trim one char front]
[trim one char back ] -> [trim one char back]
[ ] -> []
[ ] -> []
[a] -> [a]
[] -> []
*/
char *sample_strings[] =
{
"nothing to trim",
" trim the front",
"trim the back ",
" trim front and back ",
" trim one char front and back ",
" trim one char front",
"trim one char back ",
" ",
" ",
"a",
"",
NULL
};
char test_buffer[64];
char comparison_buffer[64];
size_t index, compare_pos;
for( index = 0; sample_strings[index] != NULL; ++index )
{
// Fill buffer with known value to verify we do not write past the end of the string.
memset( test_buffer, 0xCC, sizeof(test_buffer) );
strcpy( test_buffer, sample_strings[index] );
memcpy( comparison_buffer, test_buffer, sizeof(comparison_buffer));
printf("[%s] -> [%s]\n", sample_strings[index],
trim(test_buffer));
for( compare_pos = strlen(comparison_buffer);
compare_pos < sizeof(comparison_buffer);
++compare_pos )
{
if( test_buffer[compare_pos] != comparison_buffer[compare_pos] )
{
printf("Unexpected change to buffer @ index %u: %02x (expected %02x)\n",
compare_pos, (unsigned char) test_buffer[compare_pos], (unsigned char) comparison_buffer[compare_pos]);
}
}
}
return 0;
}
El archivo fuente era trim.c. Compilado con ‘cc -Wall trim.c -o trim’.
-
Yay, finalmente alguien que conoce el comportamiento indefinido de ctype.
– Roland Illig
18 de septiembre de 2016 a las 2:21
Aquí está mi intento de una función de recorte en el lugar 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.
}
-
Sugerir cambiar a
while ((end >= begin) && isspace(str[end]))
para prevenir UB cuandostr is
“”. Prevents
calle[-1]`.– chux – Reincorporar a Monica
6 de noviembre de 2013 a las 16:43
-
Por cierto, tengo que cambiar esto a str[i – begin + 1] para que funcione
– truongnm
9 de septiembre de 2016 a las 8:53
-
Tienes que lanzar el argumento para
isspace
paraunsigned char
de lo contrario, invoca un comportamiento indefinido.– Roland Illig
18 de septiembre de 2016 a las 2:00
-
@RolandIllig, ¿por qué sería un comportamiento indefinido? La función está diseñada para trabajar con caracteres.
– wovano
5 de marzo de 2020 a las 23:32
-
@wovano No, no lo es. Las funciones de
<ctype.h>
están destinados a trabajar con enteros, que representanunsigned char
o el valor especialEOF
. Consulte stackoverflow.com/q/7131026/225757.– Roland Illig
5 de marzo de 2020 a las 23:43