Estoy tratando de escribir una función de recorte idiomática en C. ¿Cómo se ve esto? ¿Debería malloc en su lugar la nueva cadena y devolverla?
void trim(const char *input, char *result) { int i, j = 0; for (i = 0; input[i] != "\0"; i++) { if (!isspace(input[i])) { result[j++] = input[i]; } } }
Comentarios
- Hay ‘ Una serie de problemas con ese código, es ‘ vulnerable a ataques de desbordamiento del búfer y no ‘ Hacer lo que hace una función típica » trim «. Recortar elimina los espacios en blanco iniciales y finales. Esto los elimina a todos.
- Gracias. ¿Puede explicar cómo manejar los ataques de desbordamiento de búfer?
- Nunca debe copiar datos a ciegas en un búfer cuando no ‘ no sepa cuánto espacio se asigna a eso, que ‘ solo está buscando problemas. Una cosa sencilla sería agregar un parámetro que tome el tamaño del búfer. De esa forma, ‘ está todo en la persona que llama para decirle qué tan grande es realmente. Luego, ‘ depende de usted nunca intentar leer / escribir más allá de la longitud dada. Por supuesto que ‘ no es infalible, la persona que llama puede darte longitudes falsas, pero eso sería un problema de su parte, no del tuyo.
Respuesta
Como señaló @JeffMercado, esto elimina los espacios en lugar de recortar los espacios iniciales y finales. Suponiendo que desea mantener la funcionalidad actual, llamémoslo remove_spaces
.
Aquí hay un error realmente sutil:
... isspace(input[i]) ...
isspace
toma el valor de un carácter sin firmar o EOF
. Pasarle un char
, que normalmente está firmado, producirá un comportamiento indefinido. En su lugar, diga:
... isspace((unsigned char) input[i]) ...
Otro error: no emite un terminador NUL, lo que significa que la persona que llama no tendría forma de saber la longitud de la cadena ( a menos que haya puesto a cero el búfer antes de llamar a su función).
Arreglar estos errores nos da:
void remove_spaces(const char *input, char *result) { int i, j = 0; for (i = 0; input[i] != "\0"; i++) { if (!isspace((unsigned char) input[i])) { result[j++] = input[i]; } } result[j] = "\0"; }
@JeffMercado también dijo esta función es vulnerable al desbordamiento del búfer. En cierto sentido, esto no es cierto, siempre que la persona que llama sepa que debe asignar un búfer de al menos strlen(input) + 1
. Pero la persona que llama puede ser perezosa y simplemente decir char result[100]
. Agregar un parámetro de tamaño de búfer de salida probablemente lo protegerá contra tal error:
void remove_spaces(const char *input, char *output, size_t output_size);
Vea si puede implementar esto . Algunas cosas a tener en cuenta:
-
No se olvide del terminador NUL cuando compruebe el tamaño del búfer de salida.
-
No sea como strncpy y omita el terminador NUL cuando tenga que truncar la cadena, ya que puede provocar errores sutiles.
-
Si usa
int
parai
yj
ysize_t
paraoutput_size
, debería recibir advertencias del compilador sobre la comparación entre firmado y no firmado. Si no lo hace, active las advertencias del compilador. Si está usando GCC desde la línea de comandos, acostúmbrese a escribirgcc -Wall -W
.
Comentarios
-
strncpy()
no es una función de cadena, incluso aunque algunos asumen. Entonces, el resultado de ser una cadena sería una casualidad de todos modos. Lo que hace que la analogía sea, en el mejor de los casos, incompleta.
Respuesta
Sabemos que podemos mover un puntero hacia adelante y hacia atrás , y también sabemos que podemos recortar una cuerda desde la izquierda. Si incrementamos el puntero y disminuimos el puntero para recortar desde la derecha, entonces dos while
bucles son suficientes. Notará que el recuento de caminatas de la derecha es menor que el de la izquierda.
Código de recorte a la derecha:
#include <stdio.h> #include <ctype.h> void trim_both(char *, char *); int main (void) { char title[100] = " My long string "; char title_t[100] = ""; (void) printf("String before left trim is:[%s]\n", title); trim_both(title, title_t); (void) printf("String after left trim is:[%s]\n", title_t); } // trim spaces from left void trim_both(char *title_p, char *title_tp) { int flag = 0; // from left while(*title_p) { if(!isspace((unsigned char) *title_p) && flag == 0) { *title_tp++ = *title_p; flag = 1; } title_p++; if(flag == 1) { *title_tp++ = *title_p; } } // from right while(1) { title_tp--; if(!isspace((unsigned char) *title_tp) && flag == 0) { break; } flag = 0; *title_tp = "\0"; } }
Responder
La forma más fácil (elimina solo los espacios):
Trim.Start:
- Compare los caracteres hasta son iguales a
" "
(espacio u otros caracteres como\n
o\t
) al comienzo de la cadena e incrementa la variable temp (i
). - Mueva el puntero sobre
i
(str+=i
). Ahora la cadena comienza desde char, que no es un carácter de espacio (o cualquier otro carácter blanco).
Trim.End:
- Haga lo mismo con Trim.Start pero desde el final de la cadena.
- Establezca el último carácter (último espacio) como
\0
.
Lo importante es que la función lleva el puntero al puntero (cadena).Esté atento a la llamada a la función: StringTrim(&p2);
char * StringTrim(char * *pointerToString) { u8 start=0, length=0; // Trim.Start: length = strlen(*pointerToString); while ((*pointerToString)[start]==" ") start++; (*pointerToString) += start; if (start < length) // Required for empty (ex. " ") input { // Trim.End: u8 end = strlen(*pointerToString)-1; // Get string length again (after Trim.Start) while ((*pointerToString)[end]==" ") end--; (*pointerToString)[end+1] = 0; } return *pointerToString; }
Uso:
char str1[] = " test1 "; char * p1 = str1; Debug("1. before trim: [%s]", p1); StringTrim(&p1); Debug("1. after trim [%s]", p1); char str2[] = " test2"; char * p2 = str2; Debug("2. before trim: [%s]", p2); StringTrim(&p2); Debug("2. after trim [%s]", p2); char str3[] = "test3 "; char * p3 = str3; Debug("3. before trim: [%s]", p3); StringTrim(&p3); Debug("3. after trim [%s]", p3); char str4[] = " "; char * p4 = str4; Debug("4. before trim: [%s]", p4); StringTrim(&p4); Debug("4. after trim [%s]", p4); char str5[] = ""; char * p5 = str5; Debug("5. before trim: [%s]", p5); StringTrim(&p5); Debug("5. after trim [%s]", p5);
Resultado :
1. before trim: [ test1 ] 1. after trim [test1] 2. before trim: [ test2] 2. after trim [test2] 3. before trim: [test3 ] 3. after trim [test3] 4. before trim: [ ] 4. after trim [] 5. before trim: [] 5. after trim []