viernes, 27 de mayo de 2016

Capítulo 30 C++: Lectura desde el teclado


Diario de un programador día 74

Lectura desde el teclado.

En esta ocasión se verá como solicitar información al usuario, guardarla en una variable y luego mostrarla en pantalla. (Al igual que en las clases anteriores con los otros lenguajes)
Para leer datos se utilizará el objeto de flujo de entrada std::cin y el operador de extracción de flujo >> para obtener los datos ingresados por el usuario.
std::cin
Para tratar de entender este objeto, se empezará con un pequeño ejemplo, el cual solicitará un número entero y luego lo mostrará en pantalla:

#include<iostream>
int main(){
  int num1;

  std::cout << "Ingresa un numero: ";
  std::cin >> num1;
  std::cout << "El numero es: " << num1 << std::endl;

return 0;
}

Otro ejemplo.  Esta vez se solicitarán dos números, se sumarán y se mostrará el resultado en pantalla.

#include <iostream>
  int main(void){
  int num1, num2;

  std::cout << "Ingresa un numero ";
  std::cin  >> num1;

  std::cout << "Ingresa otro numero ";
  std::cin  >> num2;

  std::cout << "La suma es " << num1 + num2 << std::endl;
  return 0;
  }


Y aquí otro ejemplo, pero esta vez un número y una cadena:

#include <iostream>
  int main(void){
  int num1;
  char palabra[20];

  std::cout << "Ingresa un numero  ";
  std::cin  >> num1;

  std::cout << "Ingresa una palabra  ";
  std::cin  >> palabra;

  std::cout << "Numero: " << num1 << std::endl;
  std::cout << "Palabra: " << palabra << std::endl;
  return 0;

  }


Esto al parecer se ve bien, se solicitó un número y luego una palabra, entonces el resultado fue mostrado en pantalla. Hasta hora todo bien pero, ¿si en vez de escribir una palabra escribo una frase?. Probaré con una compuesta por dos palabras, utilizaré el reconocido mensaje "hola mundo"... veamos:

La palabra "hola mundo" salió cortada, imprimiendo solamente "hola". Esto mismo ocurre con scanf() en el lenguaje C., lo que ocurre es que cin lee hasta encontrar un retorno de carro o un espacio en blanco, por lo que al dejar el espacio entre "hola mundo", simplemente ignoró todo lo que seguía después, dejando la parte sobrante en el buffer (memoria). Una solución a este problema es utilizar cin.getline(variable, nro.caracteres). Ejemplo:

#include <iostream>
    int main(void){
    char frase[50];

    std::cout << "Ingresa una frase: ";
    std::cin.getline(frase, 50);
    std::cout << "Frase: " << frase << std::endl;

    return 0;
  }


Muy bien!!! ahora si leyó la frase completa. Bueno, ahora que ya se pueden leer frases, se tratará de arreglar el programa anterior a ver qué sorpresa ocurre. En C, C++ es casi "normal" que ocurran cosas inesperadas o que una vez que se arregla una cosa, dos se echan a perder jajaja.  A ver...

#include <iostream>
  int main(void){
  int num1;
  char frase[50];

  std::cout << "Ingresa un numero ";
  std::cin  >> num1;

  std::cout << "Ingresa una frase: ";
  std::cin.getline(frase, 50);

  std::cout << "Numero: " << num1 << std::endl;
  std::cout << "Frase: " << frase << std::endl;
  return 0;
  }


A ver que acaba de ocurrir. El programa no permitió que ingresara la frase, "comiéndose" la instrucción siguiente. Esto ocurrió debido a que cin.getline, tomó o leyó (como prefieras llamar) el retorno de carro de la instrucción anterior que quedó almacenada en el buffer y la utilizó como si fuese el carácter ingresado por mi (aunque en realidad, si lo ingresé, ya que es la manera de aceptar el dato ingresado en la instrucción anterior, pero no quería que fuese utilizado de esta manera). Para arreglar este inconveniente, se puede utilizar cin.ignore(), el cual sirve para sacar o extraer lo que quedó en el buffer, en este caso el "Enter" o retorno de carro.

#include <iostream>
  int main(void){
  int num1;
  char frase[50];

  std::cout << "Ingresa un numero ";
  std::cin  >> num1;

  std::cin.ignore();
  std::cout << "Ingresa una frase: ";
  std::cin.getline(frase, 50);

  std::cout << "Numero: " << num1 << std::endl;
  std::cout << "Frase: " << frase << std::endl;
  return 0;
  }


Ahora sí. El limpiar la memoria logró el resultado buscado.

Algo que me he dado cuenta, es que estos errores de buffer, suceden a menudo que se intercalan solicitudes de ingreso entre números y cadenas. Por ejemplo, si solamente solicito datos numéricos, por lo general (siempre hay excepciones) no hay problema. Por otra parte si solamente solicito cadenas, tampoco se producen mayores inconvenientes. Colocaré un par de ejemplos de esto:

#include <iostream>
  int main(void){
  int num1, num2, num3;

  std::cout << "Ingresa un numero ";
  std::cin  >> num1;

  std::cout << "Ingresa otro numero ";
  std::cin  >> num2;

  std::cout << "Ingresa el ultimo numero ";
  std::cin  >> num3;

  std::cout << "Numero 1: " << num1 << std::endl;
  std::cout << "Numero 2: " << num2 << std::endl;
  std::cout << "Numero 3: " << num3 << std::endl;
  return 0;
  }


No hubo necesidad (aparente) de limpiar el buffer.
Otro ejemplo. Ahora con cadenas.

#include <iostream>
  int main(void){
  char cadena1[20], cadena2[20], cadena3[20];

  std::cout << "Ingresa una frase: ";
  std::cin.getline(cadena1,20);

  std::cout << "Ingresa otra frase: ";
  std::cin.getline(cadena2,20);;

  std::cout << "Ingresa la ultima frase: ";
  std::cin.getline(cadena3,20);

  std::cout << "Cadena 1: " << cadena1 << std::endl;
  std::cout << "Cadena 2: " << cadena2 << std::endl;
  std::cout << "Cadena 3: " << cadena3 << std::endl;
  return 0;
  }


Como se puede ver, esta vez cin.getline se "comporto de buena forma" y no se "comió" el retorno de carro como sí ocurrió cuando se ingresó un dato de tipo numérico.

Para terminar esto del ingreso por teclado, cabe mencionar que C++ también acepta las funciones de C, tales como scanf(), fgets() y varias más que rondan por ahí. Otra que mencionaré antes de terminar es la clase string, la cual permite(entre muchas otras funciones) utilizar la función getline(std::cin, variable), la que permite también leer frases. Un ejemplo de esta clase:

#include <iostream>
#include <string>

  int main(void){

  std::string frase;

  std::cout << "Ingresa una frase: ";
  getline(std::cin, frase);

  std::cout << "Frase: "   << frase   << std::endl;

  return 0;
  }
Poner atención en la forma en que se declara la variable, se utiliza un std::string, no siendo necesario declarar un arreglo a esa variable, ya que no es un arreglo de caracteres.


Hay mucha información en la web, lo malo es que no siempre es fácil de ubicar y muchas veces paso horas tratando de solucionar problemas como estos presentados ahora. Hasta la próxima.

Gustavo J. Cerda Nilo
Enero 2016, última actualización Mayo 2016




No hay comentarios:

Publicar un comentario

C++ El apuntador This

El apuntador This En C++, cada objeto tiene acceso a su propia dirección a través de un puntero o apuntador denominado This. Lo...