Eliminar el carácter de nueva línea final de la entrada fgets ()

8 minutos de lectura

avatar de usuario
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.

  • 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

avatar de usuario
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 primero char un '\0'. 2) size == 1 3) fgets() devoluciones NULL entonces buf 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 hecho size_t no está firmado, por lo que escribe en la memoria aleatoria. Creo que quieres usar ssize_t y comprobar ln 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 una strchr o strcspn 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 implementar strtok es usar strcspn y strspn.

    – 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') == NULLademás de “entrada demasiado larga para el búfer, error de marca”, existen otras posibilidades: Último texto en stdin 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

avatar de usuario
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.


bufferdel 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:

  1. strtok(buffer, "\n"); no logra eliminar el '\n' cuando buffer es "\n". De esta respuesta, modificada después de esta respuesta para advertir sobre esta limitación.

  2. Lo siguiente falla en raras ocasiones cuando el primero char Leído por fgets() es '\0'. Esto sucede cuando la entrada comienza con un incrustado '\0'. Entonces buffer[len -1] se convierte buffer[SIZE_MAX] acceder a la memoria ciertamente fuera del rango legítimo de buffer. 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';
    }
    
  3. sprintf(buffer,"%s",buffer); es un comportamiento indefinido: Ref. Además, no guarda ningún espacio en blanco inicial, separador o final. Ahora eliminado.

  4. [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 el strlen() 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 este strlen() Acercarse. De lo contrario, el strcspn() es una buena alternativa.

  • Gracias por la útil respuesta. Podemos usar strlen(buffer) cuando el tamaño del búfer se asigna dinámicamente usando malloc ?

    – 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 por buffer 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

avatar de usuario
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 usando malloc ?

    – 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 por buffer 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 a sprintf. Código editado.

    – William Pursell

    20 de agosto de 2021 a las 11:56


¿Ha sido útil esta solución?