La entrada estándar std::cin

En los apartados anteriores se ha aprendido a mostrar texto por pantalla. Sin embargo, eso no permite interaccionar a un usuario con un programa.

Una forma básica de interacción es aceptar datos en forma de texto vía teclado. La biblioteca iostream tiene una variable predefinida para un flujo (stream) de caracteres de entrada llamada cin (character input), lo que permite transformar secuencias de caracteres a la representación binaria de datos en memoria.

Nótese que es el proceso inverso al de cout, donde se transforma la representación binaria de un dato a una secuencia de caracteres imprimibles.

#include <iostream>

int main()
{
   std::cout << "Introduce un entero: ";
   int x;
   std::cin >> x;
   std::cout << "x=" << x << '\n';
}

Edita, compila y ejecuta el código

Analicemos este programa:

  1. #include <iostream>

    informamos al compilador nuestro deseo de utilizar funciones de E/S.

  2. int x

    definimos la variable donde deseamos almacenar el valor introducido por teclado.

  3. std::cin

    la variable cin pertenece al espacio de nombres std.

  4. >>

    operador de extracción, que permite extraer caracteres del flujo de entrada cin y transformarlos a su representación binaria interna.

Nota

Regla nemotécnica

Los operadores de extracción e inserción indican el sentido del flujo de datos. Así, sabiendo que con cin utilizamos el teclado y con cout mostramos en la consola:

  • cin >>: los caracteres fluyen desde el teclado

  • cout <<: los caracteres fluyen hacia la consola

La operación de extracción

La introducción de caracteres desde el teclado por parte del usuario es libre y, por tanto, sujeta a errores.

De cara a entender algunos de los errores que pueden producirse es importante conocer cómo se produce el proceso de extracción mediante el operador >>.

Podemos encontrarnos en las siguientes dos situaciones:

  1. El búfer asociado a cin está vacío. Es la situación más habitual.

    • El operador >> mantiene la ejecución del programa a la espera de la introducción de datos por parte del usuario.

    • Cuando el usuario introduce el carácter '\n', el operador >> comienza a transformar los caracteres introducidos a la representación binaria interna de la variable.

    • Todos los espacios en blanco (whitespace characters) al inicio del búfer son ignorados, incluidos tabuladores y retorno de línea.

    • Se transforman los caracteres hasta el siguiente espacio en blanco.

    • El resto de caracteres permanece en el búfer de cin hasta una nueva operación de extracción.

  2. El búfer asociado a cin tiene datos. En este caso, el operador >> comienza de forma inmediata la operación de extracción siguiendo las pautas descritas anteriormente respecto a la gestión de los espacios en blanco.

Veamos qué ocurre con el siguiente programa con distintos escenarios de introducción de datos. Supondremos que el usuario decide introducir los valores 3 y 5 para las variables x e y respectivamente.

#include <iostream>

int main()
{
   std::cout << "Introduce un entero: ";
   int x;
   std::cin >> x;
   std::cout << "Introduce otro entero: ";
   int y;
   std::cin >> y;
   std::cout << "x=" << x << "\ny=" << y << '\n';
}

Edita, compila y ejecuta el código

Caso 1

  1. Se muestra el mensaje "Introduce un entero: "

  2. El usuario pulsa 3 + '\n'

  3. Se muestra el mensaje "Introduce otro entero: "

  4. El usuario pulsa 5 + '\n'

  5. Se muestran por pantalla correctamente los valores de las variables x e y

Es el habitual y todo sucede como es de esperar.

Caso 2

  1. Se muestra el mensaje "Introduce un entero: "

  2. El usuario pulsa '\t' + '\t' + 3 + '\n'

  3. Se muestra el mensaje "Introduce otro entero: "

  4. El usuario pulsa '\n' + 5 + '\n'

  5. Se muestran por pantalla correctamente los valores de las variables x e y

El usuario ha introducido espacios en blanco al principio de cada una de las dos operaciones de extracción. Aunque es algo inusual, no hay ningún error pues el operador >> extrae estos caracteres del búfer ignorándolos.

Caso 3

  1. Se muestra el mensaje "Introduce un entero: "

  2. El usuario pulsa 3 + ' ' + 5 + '\n'

  3. Se muestra el mensaje "Introduce otro entero: x=3"

  4. Se muestra por pantalla "y=5"

En este caso, el usuario se ha precipitado y ha introducido inicialmente los dos valores separados por un espacio, 3 5'\n'. ¡Ese es el contenido del búfer en ese momento! Lo que ocurre es lo siguiente:

  1. El operador de extracción, tras introducir el carácter '\n' comienza el proceso de transformación, que se ve interrumpido por la presencia del espacio en blanco ' '.

  2. A continuación, se muestra el mensaje "Introduce otro entero: ".

  3. Comienza el proceso de extracción para la variable y, pero ahora el búfer de cin no está vacío, está formado por la cadena "5'\n'".

  4. Se extrae correctamente el valor 5 hacia la variable y.

  5. Comienza el proceso de inserción del búfer de cout. Nótese que, como no se ha introducido ningún carácter nueva línea, la cadena "x=3" aparece a continuación de la cadena "Introduce otro entero: ".

En definitiva, las variables x e y tienen los valores deseados, aunque el proceso ha sido atípico y los datos de salida en la consola muestran un aspecto desordenado.

Caso 4

  1. Se muestra el mensaje "Introduce un entero: "

  2. El usuario pulsa a + 3 + '\n'

  3. Se muestra el mensaje "Introduce otro entero: x=0"

  4. Se muestra el mensaje "y=0"

En este caso, el usuario se ha equivocado y ha introducido un carácter, a, que el operador de extracción es incapaz de transformar a entero. A partir de este momento, cin pasa a modo fallo (failure) y cualquier proceso de extracción posterior es obviado. Las variables oportunas se inicializan a 0 por defecto.

Es una situación de error en el programa y el funcionamiento de este será normalmente indefinido, con resultados espurios.

Caso 5

  1. Se muestra el mensaje "Introduce un entero: "

  2. El usuario pulsa 3 + a + '\n'

  3. Se muestra el mensaje "Introduce otro entero: x=3"

  4. Se muestra el mensaje "y=0"

Es un caso similar al anterior. En este caso, el operador de extracción es capaz de transformar el 3, pero a partir de este momento entra en modo fallo.

Atención

La gestión de este tipo de errores debidos a la incorrecta introducción de datos por parte del usuario es fundamental en cualquier programa comercial.

Ejemplos típicos los vemos en el relleno de formularios en páginas web, donde el programa se asegura que un número de cuenta, un DNI, etc. tiene el formato correcto.

Detectar caracteres o formatos erróneos, es una tarea relativamente compleja y sale del ámbito de la asignatura. Por ello, no lo abordaremos en este curso.

Sin embargo, conocer el proceso de extracción del operador >> puede arrojar luz para entender el porqué de resultados absurdos cuando se ejecuta un programa.

Introducción de más de un valor en la misma línea

El operador de extracción puede aparecer concatenado, evaluándose de izquierda a derecha.

#include <iostream>

int main()
{
   std::cout << "Introduce dos enteros: ";
   int x, y;
   std::cin >> x >> y;
   std::cout << "x=" << x << "\ny=" << y << '\n';
}

Edita, compila y ejecuta el código

Sería el escenario descrito en el Caso 3, con una introducción de valores por parte del usuario como 3 + ' ' + 5 + '\n'.