sfactor
Estoy tratando de obtener algunos datos del usuario y enviarlos a otra función en gcc. El código es algo como esto.
printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
Sin embargo, encuentro que tiene una nueva línea. \n
personaje al final. Entonces si entro John
termina enviando John\n
. como elimino eso \n
y enviar una cadena adecuada.
james morris
size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n')
name[ln] = '\0';
-
Probablemente lanzará una excepción si la cadena está vacía, ¿no? Como índice fuera de rango.
–Eduardo Olamisan
31 de mayo de 2013 a las 3:26
-
@EdwardOlamisan, sin embargo, la cadena nunca estará vacía.
–James Morris
14 de julio de 2013 a las 23:27
-
@James Morris En casos inusuales
fgets(buf, size, ....)
–>strlen(buf) == 0
. 1)fgets()
se lee como el primerochar
un'\0'
. 2)size == 1
3)fgets()
devolucionesNULL
entoncesbuf
el contenido puede ser cualquier cosa. (Sin embargo, el código de OP prueba NULL) Sugerir:size_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';
– chux – Reincorporar a Monica
02/07/2014 a las 14:00
-
¿Qué pasa si la cadena está vacía?
ln
sería -1, excepto por el hechosize_t
no está firmado, por lo que escribe en la memoria aleatoria. Creo que quieres usarssize_t
y comprobarln
es >0.– ablando
16/03/2015 a las 22:38
-
@legends2k: una búsqueda de un valor de tiempo de compilación (especialmente un valor cero como en
strlen
) se puede implementar de manera mucho más eficiente que una simple búsqueda de carácter por carácter. Por lo que consideraría esta solución mejor que unastrchr
ostrcspn
los basados.– Ant
29 de marzo de 2016 a las 18:28
-
Cualquier biblioteca de tiempo de ejecución de C que tenga en cuenta los subprocesos (es decir, la mayoría de las que se dirigen a una plataforma de subprocesos múltiples),
strtok()
será seguro para subprocesos (utilizará el almacenamiento local de subprocesos para el estado ‘entre llamadas’). Dicho esto, generalmente es mejor usar el no estándar (pero lo suficientemente común)strtok_r()
variante.– Michael Burr
22 de abril de 2010 a las 21:36
-
Vea mi respuesta para una variante reentrante y completamente segura para subprocesos, similar a su
strtok
enfoque (y funciona con entradas vacías). De hecho, una buena manera de implementarstrtok
es usarstrcspn
ystrspn
.– Tim Cas
11 de febrero de 2015 a las 19:09
-
Es importante manejar el caso else si se encuentra en un entorno donde existe el riesgo de filas demasiado largas. El truncamiento silencioso de la entrada puede causar errores muy dañinos.
– Malcolm McLean
08/01/2017 a las 18:52
-
Si te gustan las frases ingeniosas y usas glibc, prueba
*strchrnul(Name, '\n') = '\0';
.– dos bits
17 de agosto de 2017 a las 8:55
-
Cuando
strchr(Name, '\n') == NULL
además de “entrada demasiado larga para el búfer, error de marca”, existen otras posibilidades: Último texto enstdin
no terminó con un'\n'
o se leyó un carácter nulo incrustado raro.– chux – Reincorporar a Monica
6 de diciembre de 2017 a las 22:36
chux – Reincorporar a Monica
A continuación se muestra un enfoque rápido para eliminar un potencial '\n'
de una cadena guardada por fgets()
.
Usa strlen()
con 2 pruebas.
char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[--len] = '\0';
}
Ahora usa buffer
y len
según sea necesario.
Este método tiene el beneficio adicional de un len
valor para el código subsiguiente. Puede ser fácilmente más rápido que strchr(Name, '\n')
. Árbitro YMMV, pero ambos métodos funcionan.
buffer
del original fgets()
no contendrá en "\n"
bajo algunas circunstancias:
A) La fila era demasiado larga para buffer
tan solo char
precediendo al '\n'
se guarda en buffer
. Los caracteres no leídos permanecen en la transmisión.
B) La última línea del archivo no terminaba con un '\n'
.
Si la entrada tiene caracteres nulos incrustados '\0'
en alguna parte, la longitud reportada por strlen()
no incluirá el '\n'
localización.
Algunos otros problemas de respuestas:
-
strtok(buffer, "\n");
no logra eliminar el'\n'
cuandobuffer
es"\n"
. De esta respuesta, modificada después de esta respuesta para advertir sobre esta limitación. -
Lo siguiente falla en raras ocasiones cuando el primero
char
Leído porfgets()
es'\0'
. Esto sucede cuando la entrada comienza con un incrustado'\0'
. Entoncesbuffer[len -1]
se conviertebuffer[SIZE_MAX]
acceder a la memoria ciertamente fuera del rango legítimo debuffer
. Algo que un hacker puede intentar o encontrar al leer tontamente archivos de texto UTF16. Este era el estado de una respuesta cuando se escribió esta respuesta. Más tarde, un no OP lo editó para incluir un código como el cheque de esta respuesta para""
.size_t len = strlen(buffer); if (buffer[len - 1] == '\n') { // FAILS when len == 0 buffer[len -1] = '\0'; }
-
sprintf(buffer,"%s",buffer);
es un comportamiento indefinido: Ref. Además, no guarda ningún espacio en blanco inicial, separador o final. Ahora eliminado. -
[Edit due to good later answer] No hay problemas con el 1 liner.
buffer[strcspn(buffer, "\n")] = 0;
aparte del rendimiento en comparación con elstrlen()
Acercarse. El rendimiento en el recorte generalmente no es un problema dado que el código está haciendo E / S, un agujero negro del tiempo de CPU. Si el siguiente código necesita la longitud de la cadena o es muy consciente del rendimiento, use estestrlen()
Acercarse. De lo contrario, elstrcspn()
es una buena alternativa.
-
Gracias por la útil respuesta. Podemos usar
strlen(buffer)
cuando el tamaño del búfer se asigna dinámicamente usandomalloc
?– rrz0
10 de noviembre de 2018 a las 10:16
-
@Rrz0
buffer = malloc(allocation_size); length = strlen(buffer);
es malo: los datos en la memoria apuntados porbuffer
es desconocido.buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);
está bien– chux – Reincorporar a Monica
10 de noviembre de 2018 a las 16:15
Amitabha
Directo para eliminar el ‘\n’ de la salida de fgets si cada línea tiene ‘\n’
line[strlen(line) - 1] = '\0';
De lo contrario:
void remove_newline_ch(char *line)
{
int new_line = strlen(line) -1;
if (line[new_line] == '\n')
line[new_line] = '\0';
}
-
Gracias por la útil respuesta. Podemos usar
strlen(buffer)
cuando el tamaño del búfer se asigna dinámicamente usandomalloc
?– rrz0
10 de noviembre de 2018 a las 10:16
-
@Rrz0
buffer = malloc(allocation_size); length = strlen(buffer);
es malo: los datos en la memoria apuntados porbuffer
es desconocido.buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);
está bien– chux – Reincorporar a Monica
10 de noviembre de 2018 a las 16:15
En general, en lugar de recortar los datos que no desea, evite escribirlos en primer lugar. Si no desea la nueva línea en el búfer, no use fgets. En su lugar, utiliza getc
o fgetc
o scanf
. Tal vez algo como:
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
char Name[256];
char fmt[32];
if( snprintf(fmt, sizeof fmt, "%%%zd[^\n]", sizeof Name - 1) >= (int)sizeof fmt ){
fprintf(stderr, "Unable to write format\n");
return EXIT_FAILURE;
}
if( scanf(fmt, Name) == 1 ) {
printf("Name = %s\n", Name);
}
return 0;
}
Tenga en cuenta que este enfoque particular dejará la nueva línea sin leer, por lo que es posible que desee utilizar una cadena de formato como "%255[^\n]%*c"
descartarlo (por ejemplo, sprintf(fmt, "%%%zd[^\n]%%*c", sizeof Name - 1);
), o tal vez seguir el scanf con un getchar()
.
-
¿Se da cuenta de que el fragmento de código anterior es vulnerable a desbordamientos de búfer?
sprintf
¡no comprueba el tamaño del búfer!– Zafiro_Brick
20 de agosto de 2021 a las 3:19
-
@Sapphire_Brick Realmente no lo es. La longitud de la cadena de formato será 7 + el número de dígitos en la representación en base 10 de la longitud del nombre. Si esa longitud es superior a 24, tendrá otros problemas. Si quieres estar seguro y usar
snprintf
ciertamente podría, pero esto funcionará para búferes que son significativamente más grandes que un petabyte.– William Pursell
20 de agosto de 2021 a las 3:36
-
Para desbordar el búfer, debe crear una matriz automática de aproximadamente 8 yotta-bytes, ya que no desbordará el búfer hasta que
Name
tiene más de 2^83 bytes de tamaño. En términos prácticos, esto no es un problema. Pero si,snprintf
siempre se debe preferir asprintf
. Código editado.– William Pursell
20 de agosto de 2021 a las 11:56
if (!fgets(Name, sizeof Name, stdin))
(como mínimo, no utilice dos negaciones, ! y !=)– Roger Paté
22 de abril de 2010 a las 20:05
@Roger Pate “no use dos negaciones” –> hmmm, si profundizamos, “no” y “negación” son ambos negaciones. ;-). Tal vez “Usar
if (fgets(Name, sizeof Name, stdin)) {
.– chux – Reincorporar a Monica
24 de agosto de 2016 a las 14:53
@chux, estoy seguro de que quisiste decir
if (fgets(Name, sizeof Name, stdin) == NULL ) {
– R Sahu
5 abr 2018 a las 18:50
@RSahu Cierto: molesto
!
:– chux – Reincorporar a Monica
5 abr 2018 a las 19:30