C: ¿Cómo puedo hacer que la entrada scanf() tenga uno de dos formatos?

4 minutos de lectura

avatar de usuario
Juan

Necesito hacer este programa, que toma dos triángulos y los compara.

Básicamente, todo funciona bien, excepto la parte en la que el usuario ingresa los datos iniciales. Mi principal problema es que una de las condiciones es que el usuario puede ingresar las longitudes de los tres lados de los triángulos o las coordenadas X, Y de los tres vértices.


Necesito que funcione como cualquiera de estos:
Esta entrada significa que el usuario usó longitudes de lados:

{ 5 , 5 , 5 }

Esta entrada significa que el usuario usó las coordenadas X, Y de los vértices:

{ [ 1 ; 1 ] , [ 3 ; 1 ] , [ 2 ; 2 ] }

Aquí está mi código de cómo traté de resolverlo, pero por alguna razón si el usuario ingresa usando el vértice la primera condición, que verifica si no son las longitudes de los lados, lo estropea todo.

#include <stdio.h>

int main() {
    double a, b, c, A[2], B[2], C[2];
    char s;

    if(scanf(" { [ %lf ; %lf  ] , [ %lf ; %lf  ] , [ %lf ; %lf  ] }%c", 
            &A[0], &A[1], &B[0], &B[1], &C[0], &C[1], &s) != 7 && s != '\n') {
        s=" ";

        if(scanf(" { %lf , %lf , %lf }%c", &a, &b, &c, &s) != 4 && s != '\n') {
            printf("error\n");
            return 1;
        }

    }

    // rest of the code...

    printf("success\n");
    return 0;
}

Si cambio estas dos condiciones, cambia y funciona solo si el usuario ingresa usando el vértice …

¿Es posible hacer que de alguna manera simplemente funcione así?

  • Leería toda la línea, decidiría qué tipo de datos son y luego los analizaría con sscanf.

    – Ninja retirado

    28 oct 2018 a las 18:13

  • Múltiples preguntas sobre SO preguntan sobre este formato de datos, que incluyen: stackoverflow.com/questions/53145987; stackoverflow.com/questions/53132823; stackoverflow.com/questions/53042562; stackoverflow.com/questions/53042562; stackoverflow.com/questions/53037182; stackoverflow.com/questions/53034556: todos ellos preguntan sobre el mismo formato de datos “nuevo en SO en otoño de 2018” de una forma u otra.

    –Jonathan Leffler

    4 de noviembre de 2018 a las 22:36

  • Esta es la pregunta canónica para este formato, pero existen variantes menores en los requisitos percibidos por las personas, y si la respuesta que está buscando no está aquí, consulte las preguntas que se enumeran en “Enlazado” a la derecha. Hay al menos 9 preguntas (incluida esta) sobre el tema. El formato es casi, pero no del todo, como JSON. Entre otras diferencias, este formato utiliza un punto y coma ; donde JSON usaría una coma ,. También carece de los nombres de los pares de nombre/valor dentro del objeto ({ … }) notación en JSON.

    –Jonathan Leffler

    7 de noviembre de 2018 a las 7:52

avatar de usuario
chux – Reincorporar a Monica

Es mucho mejor usar char buf[big_enough * 2]; fgets(buf, sizeof buf, stdin) para leer el línea y luego analizarlo, tal vez con sscanf(buf, " { [ %lf ... y sscanf(buf, " { %lf ....


Sin embargo, si el código está obligado a quedarse con scanf():

OP’s primero scanf(" { [ %lf ... consume el '{' esperado en el 2do scanf( " { %lf ...

En lugar de:

if(scanf(" { [ %lf ; %lf  ] , [ %lf ; %lf  ] , [ %lf ; %lf  ] }%c", 
        &A[0], &A[1], &B[0], &B[1], &C[0], &C[1], &s) != 7 && s != '\n') {
    s=" ";

    //    no  {
    //        v
    if(scanf(" %lf , %lf , %lf }%c", &a, &b, &c, &s) != 4 && s != '\n') {
        printf("error\n");
        return 1;
    }

}

Privilegiado fgets() manera:

// Form a reasonable, yet generous buffer
#define I (50 /* rough estimate of characters use to read a double, adjust as needed */)
//                          { [ 1 ; 1 ] , [ 3 ; 1 ] , [ 2 ; 2 ] }\n\0
#define LINE_SIZE_EXPECTED (4 + I+3+I  +7  +I+3+I  +7  +I+3+I+6)
char buf[LINE_SIZE_EXPECTED * 2]; // Lets us use 2x for extra spaces, leading zeros, etc.

if (fgets(buf, sizeof buf, stdin)) {
  // Consider using "%n" to detect a complete scan and check for no trailing junk
  int n = 0;
  sscanf(buf, " { [ %lf ; %lf  ] , [ %lf ; %lf  ] , [ %lf ; %lf  ] } %n",
      &A[0], &A[1], &B[0], &B[1], &C[0], &C[1], &n);
  if (n && buf[n] == '\0') {
    // successful scan
  } else {
    n = 0;
    sscanf(" { %lf , %lf , %lf } %n", &a, &b, &c, &n);
    if (n && buf[n] == '\0') {
      // successful scan
    } else
      // both scans failed
    }
  }
}

Deberías usar sscanf.

El seguimiento code podría funcionar:

#include <stdio.h>

int main() {
    double a, b, c, A[2], B[2], C[2];
    char *s = NULL;
    size_t n = 0;

    getline(&s, &n, stdin);

    if(sscanf(s, " { [ %lf ; %lf  ] , [ %lf ; %lf  ] , [ %lf ; %lf  ] }", &A[0], &A[1], &B[0], &B[1], &C[0], &C[1]) != 6
        && sscanf(s, " { %lf , %lf , %lf }", &a, &b, &c) != 3) {

        printf("error\n");
        return 1;
    }

    // rest of the code...

    printf("success\n");
    return 0;
}

  • Probablemente necesite capturar el valor de retorno de sscanf() en ambas llamadas para que el resto del código sepa qué datos mirar. Sin embargo, leer una línea y luego escanearla dos veces si es necesario es el camino a seguir.

    –Jonathan Leffler

    28/10/2018 a las 18:30

  • También agregue algunos otros controles de valor de retorno.

    – Antti Haapala — Слава Україні

    28/10/2018 a las 18:31

  • Sólo comprobando la devolución como en scanf() == 6 no 1) se asegurará de que la línea termine con "] }" ni 2) no tiene otro texto final inesperado.

    – chux – Reincorporar a Monica

    28 oct 2018 a las 18:47


  • Solo una pequeña cosa: getline no es C estándar, es POSIX.

    –Daniel Kamil Kozar

    28 oct 2018 a las 18:56

¿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