Validación de datos






Es frecuente construir programas dotados de un menú; las opciones del menú suelen estar controladas mediante letras o dígitos individuales, y esto hace surgir un problema debido a la presencia de retornos de carro y otros caracteres no deseados en el búfer de teclado. Una posible solución es la que puede verse en el ejemplo siguiente:

#include<stdio.h>
#include<stdlib.h>

int main(int argc, char * argv[])
 {
  char temp[10];
  int len;
  do
   {
    system("clear");
    printf("Escriba una opción ");
    fgets(temp, sizeof(temp), stdin);
    len = strlen(temp);
    if (len != 2)
     fpurge(stdin);
   } while (len != 2);

  printf("\n\nLa opción seleccionada era: %c\n", temp[0]);
  
  printf("\n\nTerminación normal\n\n");
}

Este programa descarta la cadena leída (y de hecho todo el contenido del búfer) cuando no se proporciona una cadena de longitud 2, que debe estar formada por una opción y un retorno de carro. Si el usuario pulsa un retorno de carro, no se admite (es una cadena de longitud 1). Si el usuario pulsa tres o más caracteres, no se admite tampoco, y se descarta mediante fpurge el contenido del búfer. Entonces el usuario se ve obligado a introducir carácter y un retorno de carro, y no quedan caracteres adicionales en el búfer.
El carácter leído puede analizarse después, mediante una sentencia switch() , o bien se puede comparar directamente su valor con los almacenados en una cadena de referencia, mediante una llamada a la función strchr() .

Validación de valores numéricos






Las funciones scanf() , sscanf() , fscanf() y similares poseen una característica interesante: proporcionan el número de conversiones efectuadas. Esto se puede aprovechar para determinar si lo que ha insertado el usuario era realmente un número. Véase el programa siguiente:

#include<stdio.h>
#include<stdlib.h>

int main(int argc, char * argv[])
 {
  int n;
  int resultado;
  do
   {
    system("clear");
    printf("Deme un entero: ");
    resultado = scanf("%d", &n);
    fpurge(stdin);
   } while (resultado != 1);
  printf("El número escrito era: %d\n", n);  
 }
Si se compila y ejecuta este programa, se observará que al escribir una cadena traducible a un valor numérico el programa funciona correctamente; si se inserta un carácter alfanumérico, sin embargo, el programa vuelve a solicitar un entero. Internamente, si llega a producirse una conversión, entonces resultado vale 1; en caso contrario, vale 0. Aprovechando esta circunstancia, construimos un bucle del cual sólo será posible salir cuando se produzca una conversión, esto es, cuando el usuario escriba un valor numérico.
Lamentablemente, los resultados obtenidos de esta manera no son totalmente correctos. Considérense, por ejemplo, los resultados de la ejecución siguiente:
[maxus:~] coti% ./a.out
Deme un entero: 999999999999999999999999999999999
El número escrito era: -1
[maxus:~] coti%



Lamentablemente, no se ha capturado el error de desbordamiento, y lo que es peor, no se ha producido una indicación de error. El problema puede abordarse empleando dos métodos: detectar el error, o bien rechazar valores incorrectos. El primer método requiere utilizar la variable errno, que recibe valor cuando el sistema de ejecución detecta una situación anormal. Esto podría hacerse empleando un programa como el siguiente:

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

int main(int argc, char * argv[])
 {
  int n;
  int resultado;
  int error;
  do
   {
    /*system("clear");*/
    printf("Deme un entero: ");
    resultado = scanf("%d", &n);
    error = errno;
    if (resultado == 0)
     printf("\n\nNo hubo conversión.\n\n");
    if (error != 0)
     perror("errno no nulo");
    clearerr(stdin);
    fpurge(stdin);
   } while (resultado ==0 || error != 0);
  printf("El número escrito era: %d\n", n);  
 }
En este programa se detectan las ausencias de conversión, como en el anterior, pero también problemas debidos, por ejemplo, a la inserción de un número correcto pero excesivamente grande. Con todo, se observa que el objetivo perseguido no es la detección del error, sino asegurar que el resultado obtenido finalmente sea correcto. La corrección tiene dos facetas: el valor producido debe ser un número y además ese número debe encontrarse entre dos valores (mínimo y máximo) admisibles para el usuario. Esto puede lograrse empleando un programa como el siguiente:

#include<stdio.h>
#include<stdlib.h>

int valor_entero(int minimo, int maximo, char * peticion);

int main(int argc, char * argv)
 {
  int numero;
  numero = valor_entero(0,10,"Escriba el número de copias: ");
  printf("\n\nEl valor proporcionado es: %d\n");
  return 0;
 }

int valor_entero(int minimo, int maximo, char * peticion)
 {
  int temp;
  do
   {
    printf("%s (entre %d y %d)", peticion, minimo, maximo);
    scanf("%d", &temp);
    fpurge(stdin);
   } while (temp < minimo || temp > maximo);
  return temp;
}

En este programa se hace uso de la función valor_entero() , que nos permite especificar tres datos:



La función sólo retorna cuando el usuario proporciona un valor comprendido entre los límites deseados. Se podría emplear el valor mínimo 0 para indicar, por ejemplo, que el usuario no desea realizar copias.

Ejercicios propuestos



  1. Ejercicio 0408r01.- Escribir un programa de cálculo de IVA. El porcentaje del impuesto no será menor qu el 6% ni mayor que el 33%. Se solicitará al usuario el valor de la base imponible y el del impuesto, proporcionando como resultado el importe total del artículo.

  2. Ejercicio 0408r02.- Escribir un programa de paso de euros (€) a dólares (DUSA). El programa admitirá un tipo de cambio razonable, esto es, no negativo ni excesivamente grande.

  3. Ejercicio 0408r03.- Escribir un programa que admita un nombre, un peso y una talla. El programa debe evitar errores como la confusión entre peso y talla (una persona que pese 1,8 Kg y mida 60 metros).

  4. Ejercicio 0408r04.- Escribir un programa que admita fichas de inventario para una empresa. Las fichas constarán de campos dotados de unas ciertas características que deben cumplir: Cada ficha, una vez leída, podrá ser admitida o corregida. Las fichas, una vez almacenadas, se escriben en un archivo de texto con formato encolumnado o delimitado, a elección del usuario.

  5. Ejercicio 0408r05.- Escribir un programa que admita registros escritos en un archivo de texto, con formato encolumnado o delimitado. El programa debe examinar los valores de los campos de todas las líneas, comunicando al usuario la existencia de un error en caso de que alguno de los campos no satisfaga ciertas condiciones, dadas por el usuario antes de examinar el archivo. En caso de error, se admitirá la corrección de la línea.

  6. Ejercicio 0408r06.- Se dispone de una lista de registros de personal. En cada ficha hay campos de nombre, edad, talla, peso y aficiones. Se pide construir un programa adecuado para admitir registros de este tipo, comprobando la validez de sus campos. Los registros, una vez admitidos (y corregidos en caso de error) se almacenarán en un archivo de formato binario.

  7. Ejercicio 0408r07.- Se dispone de una lista de registros de personal. En cada ficha hay campos de nombre, edad, talla, peso y aficiones. Los registros forman parte de un archivo de formato binario. Se pide construir un programa adecuado para leer esos archivos, verificar la corrección de sus campos, corregirlos si fuera preciso y almacenar el resultado en un archivo de formato encolumnado o delimitado, a elección del usuario.

  8. Ejercicio 0408r08.- Una base de datos formada por registros binarios está produciendo errores porque los campos se introdujeron sin verificación. Se pide construir un programa que depure esa base de datos, aplicando una corrección a los campos defectuosos. Concretamente los valores numéricos que estén fuera del intervalo correcto recibirán el valor (máximo o mínimo) más próximo al que tuvieran. Los valores alfanuméricos de longitud 0 recibirán el valor "*". El resultado debe ser un nuevo archivo binario de formato idéntico al primero, con las correcciones ya aplicadas.