fgets no funciona después de scanf [duplicate]

9 minutos de lectura
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void delspace(char *str);

int main() {
    int i, loops;
    char s1[101], s2[101];

    scanf("%d", &loops);

    while (loops--) {
        fgets(s1, 101, stdin);
        fgets(s2, 101, stdin);
        s1[strlen(s1)] = '\0';
        s2[strlen(s2)] = '\0';

        if (s1[0] == '\n' && s2[0] == '\n') {
            printf("YES\n");
            continue;
        }

        delspace(s1);
        delspace(s2);

        for (i = 0; s1[i] != '\0'; i++)
            s1[i] = tolower(s1[i]);

        for (i = 0; s2[i] != '\0'; i++)
            s2[i] = tolower(s2[i]);

        if (strcmp(s1, s2) == 0) {
            printf("YES\n");
        }
        else {
            printf("NO\n");
        }
    }

    return 0;
}

void delspace(char* str) {
    int i = 0;
    int j = 0;
    char sTmp[strlen(str)];

    while (str[i++] != '\0') {
        if (str[i] != ' ') {
            sTmp[j++] = str[i];
        }
    }
    sTmp[j] = '\0';
    strcpy(str, sTmp);
}

Después de ingresar “bucles”, “s1” se le asignó una línea en blanco automáticamente. ¿Cómo sucede? Estoy seguro de que mi teclado funciona bien.

  • “Estoy seguro de que mi teclado funciona bien”. ¡Lol!

    – orlp

    6 mayo 2011 a las 23:38

  • Parece que aquí se hace alguna variante de esta pregunta cada día más o menos.

    –Jim Balter

    7 de mayo de 2011 a las 0:10

  • La función delspace definitivamente está mal! Esto convertirá abc\0 para bc\0.

    – CiertoY

    17 de febrero de 2016 a las 23:29

  • Regla general: para entradas interactivas siempre lea líneas.

    –Karoly Horvath

    3 de septiembre de 2016 a las 11:06

  • Relacionado: stackoverflow.com/questions/5240789/…

    – melpomene

    1 oct 2019 a las 17:54

scanf() lee exactamente lo que le pediste, dejando lo siguiente \n desde el final de esa línea en el búfer donde fgets() lo leerá Haga algo para consumir la nueva línea, o (mi solución preferida) fgets() y luego sscanf() de esa cadena.

  • si quiero usar scanf(), ¿cómo puedo consumir la nueva línea? Gracias.

    – Vain

    6 mayo 2011 a las 23:59

  • Usa algo como %*[^\n]%*c al final del formato para omitir cualquier carácter hacia arriba en la nueva línea, seguido de la propia nueva línea.

    – geekosaur

    7 de mayo de 2011 a las 0:07

  • @geekosaur: Solo virando %*[^\n]%*c al final del formato no funcionará si NO hay caracteres después de la lectura anterior y antes de la nueva línea, ya que entonces el %*[^\n] no coincidirá, por lo que el %*c se omitirá y la nueva línea seguirá estando en la entrada. Tienes que hacer el %*c en una llamada scanf separada para que funcione.

    – Chris Dodd

    16 de mayo de 2016 a las 1:59


avatar de usuario
Hugo

scanf deja espacios en blanco en el búfer de entrada, incluidos los caracteres de nueva línea. Para usar fgets para leer la siguiente línea, debe eliminar manualmente el resto de la línea actual:

int c;
do{
    c = getchar();
}while(c != EOF && c != '\n');

  • Sí, eso también funcionará, pero personalmente solo uso fgets y luego sscanf (como lo sugiere geekosaur) porque funciona “uniformemente” para todo tipo de datos… no solo caracteres individuales. Y me gustan las “soluciones genéricas, polivalentes”. Salud. Keith.

    – Corlettk

    7 de mayo de 2011 a las 0:32

  • Sin embargo, esto funciona para más de un personaje, para eso está el bucle;)

    – hugomg

    7 de mayo de 2011 a las 1:22

  • while(c != EOF && c != '\n'); no es bueno. c está fuera de alcance.

    – chux – Reincorporar a Monica

    23 de febrero de 2018 a las 0:50

  • Iba a disculparme diciendo que debo haber estado mezclando las reglas de alcance de C y Lua, pero aparentemente esta respuesta es anterior a aprender Lua, jajaja.

    – hugomg

    23 de febrero de 2018 a las 2:45


avatar de usuario
Shubham Pawar

Esta es una solución más simple

scanf("%d",&loops);
while ((getchar()) != '\n'); //This will consume the '\n' char
//now you're free to use fgets
fgets(string,sizeof(string),stdin);

avatar de usuario
corlettk

Geekosaur ha respondido bien a su pregunta, solo estoy señalando otro “problema” con su código.

La línea s1[strlen(s1)] = '\0'; es un sin operación Si s1 ya está correctamente terminado en nulo ANTES de que se ejecute.

Pero si s1 NO está correctamente terminado en nulo ANTES de que se ejecute esta línea (y no tiene suerte), causará:

  • un SIGSEGV en un sistema POSIX (*nix).
  • un GPF en Windows

Esto es porque strlen básicamente encuentra el índice de la terminador nulo existente y lo devuelve! Aquí hay una implementación válida y no optimizada de strlen:

int strlen(char *string) {
    int i = 0;
    while(string[i] != '\0') {
        ++i;
    }
    return i;
}

Entonces… Si REALMENTE le preocupa que las cadenas NO terminen en nulo, entonces haría algo como:

  • string[sizeof(string)]='\0'; sobre local cadenas automáticas (donde el compilador “sabe” el tamaño de la cadena);
  • o string[SIZE_OF_STRING] para todas las demás cadenas, donde SIZE_OF_STRING es (más comúnmente) un #define‘d constante, o una variable que mantiene específicamente para almacenar el TAMAÑO actual (no la longitud) de una cadena asignada dinámicamente.

Y si está REALMENTE, REALMENTE, REALMENTE preocupado de que las cadenas no terminen en nulo (como si estuviera tratando con métodos de biblioteca “sucios” (como ATMI de Tuxedo, por ejemplo) ADEMÁS “borre” sus “cadenas de retorno” antes de pasarlas a los métodos de la biblioteca sospechosa con:

  • antes de:memset(string, NULL, SIZE_OF_STRING);
  • invocar: DirtyFunction(/*out*/string);
  • después: string[SIZE_OF_STRING]='\0'

Los SIG11 son una completa biatch para encontrar porque (a menos que los “enganche” con un procesador de señal y dicen lo contrario, hacen que Unix finalice su programa, por lo que no puede registrar nada (después del hecho) para ayudar a averiguar de dónde diablos vino eso … especialmente considerando que en muchos casos la línea de código que lanza el SIG11 no está ni cerca de la causa real de que la cadena pierda su terminador nulo.

¿Tiene eso sentido para ti?

PD: ADVERTENCIA: strncpy NO siempre termina en nulo… probablemente quisiste decir strlcpy en cambio. Aprendí esto de la manera más difícil… cuando colapsó una facturación de 60 millones de dólares.


EDITAR:

FYI: aquí hay una versión “segura” (no optimizada) de strlen a la que llamaré strnlen (Creo que esto debería estar en cadena.h. Suspiro.).

// retuns the length of the string (capped at size-1)
int strnlen(char *string, int size) {
    int i = 0;
    while( i<size && string[i]!='\0' ) {
        ++i;
    }
    return i;
}

avatar de usuario
fantasma_ab

sólo hay que poner
scanf("%d\n",&loops);

en vez de
scanf("%d",&loops);

  • Si bien esta respuesta cubre un fenómeno importante del problema original, no brinda una idea del problema en sí. Por lo tanto, es útil en el mejor de los casos para resolver el problema original y no es realmente útil a largo plazo. Agregue más explicaciones para que su respuesta sea útil para los lectores posteriores que buscan problemas similares.

    – rpy

    12 de septiembre de 2016 a las 13:48

  • No coloque espacios en blanco finales al final de un scanf() cadena de formato: rompe por completo la UI/UX porque la entrada no se detiene hasta que escribe algo que no sea un espacio en blanco (y las nuevas líneas son espacios en blanco). Significa que el usuario tiene que predecir cuál debe ser la próxima entrada. Ver [What is the effect of trailing white space in a scanf() format string?](stackoverflow.com/q/19499060/10ths 5168)

    –Jonathan Leffler

    8 ene a las 15:22

avatar de usuario
ChandanK

Otra forma de ignorar el siguiente carácter de nueva línea (debido a presionar ENTER) después de escanear el número entero en los bucles de etiquetas variables es:

scanf ("%d%*c", &loops);

Donde, según las páginas man:

* Suprime la asignación. La conversión que sigue se produce como de costumbre, pero no se utiliza ningún puntero; el resultado de la conversión simplemente se descarta.

Esto es muy poco probable, pero es un buen hábito comprobar si hay errores durante el escaneo:

errno = 0
scanf ("%d%*c", &loops);
if (errno != 0) perror ("scanf");
// act accordingly to avoid un-necessary bug in the code

Por ejemplo, en su código, los bucles son una variable local no inicializada que contiene basura. Si scanf no logra llenar el valor deseado, el siguiente ciclo while puede ejecutarse arbitrariamente.

  • Si bien esta respuesta cubre un fenómeno importante del problema original, no brinda una idea del problema en sí. Por lo tanto, es útil en el mejor de los casos para resolver el problema original y no es realmente útil a largo plazo. Agregue más explicaciones para que su respuesta sea útil para los lectores posteriores que buscan problemas similares.

    – rpy

    12 de septiembre de 2016 a las 13:48

  • No coloque espacios en blanco finales al final de un scanf() cadena de formato: rompe por completo la UI/UX porque la entrada no se detiene hasta que escribe algo que no sea un espacio en blanco (y las nuevas líneas son espacios en blanco). Significa que el usuario tiene que predecir cuál debe ser la próxima entrada. Ver [What is the effect of trailing white space in a scanf() format string?](stackoverflow.com/q/19499060/10ths 5168)

    –Jonathan Leffler

    8 ene a las 15:22

scanf() deje el siguiente \n desde el final de esa línea en el búfer donde fgets() lo leerá Para resolver esto, tenemos que hacer algo para consumir la nueva línea. Veamos el problema.

El problema

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    scanf("%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
}

Producción:

Enter in scanf: Hello
Enter in fgets:

Como puede ver, no muestra la parte fgets. Veamos la solución.

Poner getchar() entre escaneo y fgets()

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    scanf("%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
}

Producción:

Enter in scanf: Hello
Enter in fgets: Hello

Si es posible usar scanf() después de la fgets()

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    fgets(input, 10, stdin);
    getchar();
    printf("Enter in scanf: ");
    scanf("%s", input);
}

Producción:

Enter in fgets: Hello
Enter in scanf: Hello

Poner fget() 2 veces (este método no funciona en algún momento)

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    scanf("%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
    fgets(input, 10, stdin)
}

Producción:

Enter in scanf: Hello
Enter in fgets: Hello

Poner sscanf() insertado de scanf()

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    sscanf(hello, "%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
}

Producción:

Enter in sscanf: Hello
Enter in fgets: Hello

  • ¿Te importaría elaborar más sobre esto? Por ejemplo, proporcionar un ejemplo.

    – Elis Byberi

    3 de marzo de 2020 a las 18:37

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad