¿Por qué llamaríamos a cin.clear() y cin.ignore() después de leer la entrada?

14 minutos de lectura

¿Por que llamariamos a cinclear y cinignore despues de leer
jakeown

Tutorial de C++ de Google Code University solía tener este código:

// Description: Illustrate the use of cin to get input
// and how to recover from errors.

#include <iostream>
using namespace std;

int main()
{
  int input_var = 0;
  // Enter the do while loop and stay there until either
  // a non-numeric is entered, or -1 is entered.  Note that
  // cin will accept any integer, 4, 40, 400, etc.
  do {
    cout << "Enter a number (-1 = quit): ";
    // The following line accepts input from the keyboard into
    // variable input_var.
    // cin returns false if an input operation fails, that is, if
    // something other than an int (the type of input_var) is entered.
    if (!(cin >> input_var)) {
      cout << "Please enter numbers only." << endl;
      cin.clear();
      cin.ignore(10000,'\n');
    }
    if (input_var != -1) {
      cout << "You entered " << input_var << endl;
    }
  }
  while (input_var != -1);
  cout << "All done." << endl;

  return 0;
}

¿Cuál es el significado de cin.clear() y cin.ignore()? ¿Por qué son los 10000 y \n parámetros necesarios?

  • Esta es mi publicación más votada de todos los tiempos con diferencia. Debo haber alcanzado mi punto máximo en la escuela secundaria.

    – JacKeown

    8 oct 2020 a las 19:56

los cin.clear() borra el indicador de error en cin (para que futuras operaciones de E/S funcionen correctamente), y luego cin.ignore(10000, '\n') salta a la siguiente nueva línea (para ignorar cualquier otra cosa en la misma línea que el no número para que no cause otra falla de análisis). Solo omitirá hasta 10000 caracteres, por lo que el código asume que el usuario no ingresará una línea muy larga e inválida.

  • +1. Quiero agregar que en lugar de ignorar hasta 10000 caracteres, sería mejor usar cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');.

    -Dennis

    2 de julio de 2013 a las 17:05


  • Si quieres usar std::numeric_limitsasegurate que #include <limits>.

    – gbmhunter

    7 de abril de 2014 a las 0:58

  • alguien me puede explicar la sintaxis std::numeric_limits<std::streamsize>::max()?

    – Minh Tran

    9 de julio de 2017 a las 16:56

  • @Minh Tran: std::streamsize tiene un signo integral que proporciona el número de caracteres de E/S transferidos o el tamaño del búfer de E/S. Aquí, al usar la clase de plantilla “numeric_limits” queríamos saber el límite máximo del búfer de E/S o el carácter transferido.

    – Pankaj

    21 oct 2017 a las 10:15

  • @Minh Tran: solo una corrección menor “streamsize” no es una clase, es solo un tipo integral. También podemos obtener el límite de otros, es decir. int, char, etc. por favor verifíquelo en https://stackoverflow.com/questions/5131647/why-would-we-call-cin-clear-and-cin-ignore-after-reading-input (es.cppreference.com/w/cpp/types/numeric_limits)

    – Pankaj

    23 de octubre de 2017 a las 2:41


Entras en el

if (!(cin >> input_var))

declaración si se produce un error al tomar la entrada de cin. Si se produce un error, se establece un indicador de error y los futuros intentos de obtener información fallarán. por eso necesitas

cin.clear();

para deshacerse de la bandera de error. Además, la entrada que falló estará en lo que supongo que es una especie de búfer. Cuando intente obtener una entrada nuevamente, leerá la misma entrada en el búfer y fallará nuevamente. por eso necesitas

cin.ignore(10000,'\n');

Saca 10000 caracteres del búfer pero se detiene si encuentra una nueva línea (\n). El 10000 es solo un gran valor genérico.

  • Solo agregue que el valor predeterminado para ignorar es omitir un solo carácter, por lo que necesita un número mayor para omitir una línea completa.

    – Bo Person

    27 de febrero de 2011 a las 9:45

¿Por qué usamos:

1) cin.ignorar

2) cin.claro

?

Simplemente:

1) Para ignorar (extraer y descartar) valores que no queremos en la transmisión

2) Para borrar el estado interno de la corriente. Después de usar cin.clear, el estado interno se establece nuevamente en goodbit, lo que significa que no hay ‘errores’.

Versión larga:

Si algo se pone en ‘stream’ (cin), entonces debe tomarse desde allí. Por ‘tomado’ nos referimos a ‘usado’, ‘retirado’, ‘extraído’ del flujo. La corriente tiene un flujo. Los datos fluyen en cin como agua en la corriente. Simplemente no puedes detener el flujo de agua;)

Mira el ejemplo:

string name; //line 1
cout << "Give me your name and surname:"<<endl;//line 2
cin >> name;//line 3
int age;//line 4
cout << "Give me your age:" <<endl;//line 5
cin >> age;//line 6

¿Qué sucede si el usuario responde: “Arkadiusz Wlodarczyk” en la primera pregunta?

Ejecute el programa para verlo usted mismo.

Verá en la consola “Arkadiusz”, pero el programa no le preguntará por la “edad”. Simplemente terminará inmediatamente después de imprimir “Arkadiusz”.

Y “Wlodarczyk” no se muestra. Parece como si se hubiera ido (?)*

¿Qué sucedió? 😉

Porque hay un espacio entre “Arkadiusz” y “Wlodarczyk”.

El carácter de “espacio” entre el nombre y el apellido es una señal para la computadora de que hay dos variables que esperan ser extraídas en el flujo de “entrada”.

La computadora cree que está tratando de enviar a la entrada más de una variable. Ese signo de “espacio” es un signo para que él lo interprete de esa manera.

Entonces, la computadora asigna “Arkadiusz” a ‘nombre’ (2) y debido a que coloca más de una cadena en la transmisión (entrada), la computadora intentará asignar el valor “Wlodarczyk” a la variable ‘edad’ (!). El usuario no tendrá la oportunidad de poner nada en el ‘cin’ en la línea 6 porque esa instrucción ya se ejecutó (!). ¿Por qué? Porque todavía quedaba algo en la corriente. Y como dije anteriormente, la corriente está en un flujo, por lo que todo debe eliminarse lo antes posible. Y la posibilidad vino cuando la computadora vio la instrucción cin >> edad;

La computadora no sabe que creaste una variable que almacena la edad de alguien (línea 4). ‘edad’ es simplemente una etiqueta. Pues la ‘era’ de la informática bien podría llamarse: ‘afsfasgfsagasggas’ y sería lo mismo. Para él, es solo una variable a la que intentará asignar “Wlodarczyk” porque ordenó/instruyó a la computadora que lo hiciera en la línea (6).

Está mal hacerlo, pero ¡oye, eres tú quien lo hizo! ¡Es tu culpa! Bueno, tal vez el usuario, pero aún así…


Bien, bien. ¡¿Pero cómo solucionarlo?!

Intentemos jugar un poco con ese ejemplo antes de arreglarlo correctamente para aprender algunas cosas más interesantes 🙂

Prefiero hacer un acercamiento donde entendemos las cosas. Arreglar algo sin saber cómo lo hicimos no da satisfacción, ¿no crees? 🙂

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate(); //new line is here :-)

Después de invocar el código anterior, notará que el estado de su transmisión (cin) es igual a 4 (línea 7). Lo que significa que su estado interno ya no es igual a goodbit. Algo está mal. Es bastante obvio, ¿no? Intentó asignar un valor de tipo de cadena (“Wlodarczyk”) a la variable de tipo int ‘edad’. Los tipos no coinciden. Es hora de informar que algo anda mal. Y la computadora lo hace cambiando el estado interno de la corriente. Es como: “Estás jodido, hombre, arréglame por favor. Te lo informo ‘amablemente’ ;-)”

Simplemente ya no puedes usar ‘cin’ (stream). Está atorado. Como si hubieras puesto grandes troncos de madera en la corriente de agua. Debe arreglarlo antes de poder usarlo. Los datos (agua) ya no se pueden obtener de esa corriente (cin) porque el registro de madera (estado interno) no le permite hacerlo.

Oh, entonces, si hay un obstáculo (troncos de madera), ¿podemos quitarlo usando herramientas hechas para hacerlo?

¡Sí!

el estado interno de cin establecido en 4 es como una alarma que está aullando y haciendo ruido.

cin.clear limpia el estado de vuelta a la normalidad (goodbit). Es como si hubieras venido y silenciado la alarma. Simplemente lo postergas. Sabes que algo pasó así que dices: “Está bien que dejes de hacer ruido, sé que algo anda mal ya, cállate (claro)”.

¡Está bien, hagámoslo! Usemos cin.clear().

Invoque el siguiente código usando “Arkadiusz Wlodarczyk” como primera entrada:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl; 
cin.clear(); //new line is here :-)
cout << cin.rdstate()<< endl;  //new line is here :-)

Seguramente podemos ver después de ejecutar el código anterior que el estado es igual a goodbit.

Genial entonces el problema esta solucionado?

Invoque el siguiente código usando “Arkadiusz Wlodarczyk” como primera entrada:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl;; 
cin.clear(); 
cout << cin.rdstate() << endl; 
cin >> age;//new line is here :-)

Aunque el estado se establece en goodbit después de la línea 9, al usuario no se le pregunta por la “edad”. El programa se detiene.

¡¿POR QUÉ?!

Oh hombre… Acabas de posponer la alarma, ¿qué pasa con el tronco de madera dentro del agua?* Vuelve al texto donde hablamos sobre “Wlodarczyk” y cómo supuestamente desapareció.

Debes quitar “Wlodarczyk” ese trozo de madera del arroyo. Apagar las alarmas no soluciona el problema en absoluto. ¿Acabas de silenciarlo y crees que el problema se ha ido? 😉

Así que es hora de otra herramienta:

cin.ignore se puede comparar con un camión especial con cuerdas que viene y quita los troncos de madera que atascaron el arroyo. Borra el problema que creó el usuario de su programa.

Entonces, ¿podríamos usarlo incluso antes de que suene la alarma?

Sí:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;

El “Wlodarczyk” se eliminará antes de hacer el ruido en la línea 7.

¿Qué es 10000 y ‘\n’?

Dice eliminar 10000 caracteres (por si acaso) hasta que se cumpla ‘\n’ (ENTRAR). Por cierto, se puede hacer mejor usando numeric_limits pero no es el tema de esta respuesta.


Entonces, la causa principal del problema desapareció antes de que se hiciera ruido…

¿Por qué necesitamos ‘claro’ entonces?

¿Qué pasaría si alguien hubiera pedido la pregunta ‘dame tu edad’ en la línea 6, por ejemplo: “veinte años” en lugar de escribir 20?

Los tipos no vuelven a coincidir. La computadora intenta asignar una cadena a int. Y comienza la alarma. Ni siquiera tienes la oportunidad de reaccionar en una situación como esa. cin.ignore no te ayudará en ese caso.

Entonces debemos usar clear en casos como ese:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;
cin.clear();
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

Pero, ¿debería borrar el estado ‘por si acaso’?

Por supuesto que no.

Si algo sale mal (cin >> age;), la instrucción le informará al respecto devolviendo falso.

Entonces podemos usar una declaración condicional para verificar si el usuario puso un tipo incorrecto en la transmisión

int age;
if (cin >> age) //it's gonna return false if types doesn't match
    cout << "You put integer";
else
    cout << "You bad boy! it was supposed to be int";

Muy bien, entonces podemos solucionar nuestro problema inicial como, por ejemplo, que:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

int age;
cout << "Give me your age:" << endl;
if (cin >> age)
  cout << "Your age is equal to:" << endl;
else
{
 cin.clear();
 cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
 cout << "Give me your age name as string I dare you";
 cin >> age;
}

Por supuesto, esto se puede mejorar, por ejemplo, haciendo lo que hizo en cuestión usando loop while.

PRIMA:

Quizás te estés preguntando. ¿Qué pasa si quiero obtener nombre y apellido en la misma línea del usuario? ¿Es posible usar cin si cin interpreta cada valor separado por “espacio” como variable diferente?

Claro, puedes hacerlo de dos maneras:

1)

string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;

cout << "Hello, " << name << " " << surname << endl;

2) o usando la función getline.

getline(cin, nameOfStringVariable);

y asi es como se hace:

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

La segunda opción podría ser contraproducente en caso de que la use después de usar ‘cin’ antes de getline.

Vamos a ver:

a)

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endl;

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Si pones “20” como edad no te pedirá NombreYApellido.

Pero si lo haces así:

B)

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endll

todo esta bien.

¡¿QUÉ?!

Cada vez que pone algo en la entrada (transmisión), deja al final el carácter blanco que es ENTER (‘\ n’). De alguna manera, debe ingresar valores en la consola. Entonces debe suceder si los datos provienen del usuario.

b) las características de cin es que ignora los espacios en blanco, por lo que cuando lee información de cin, el carácter de nueva línea ‘\n’ no importa. Se ignora.

a) la función getline obtiene la línea completa hasta el carácter de nueva línea (‘\n’), y cuando el carácter de nueva línea es lo primero, la función getline obtiene ‘\n’, y eso es todo. Extrae el carácter de nueva línea que dejó en la transmisión el usuario que puso “20” en la transmisión en la línea 3.

Entonces, para solucionarlo, invoque siempre cin.ignore(); cada vez que use cin para obtener algún valor si alguna vez va a usar getline() dentro de su programa.

Entonces el código correcto sería:

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cin.ignore(); // it ignores just enter without arguments being sent. it's same as cin.ignore(1, '\n') 
cout << "Your age is" << age << endl;


string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Espero que las transmisiones sean más claras para ti.

Hah silencio me por favor! 🙂

  • No soy op y sé que fue hace algunos años, pero fue muy informativo. ¡Gracias!

    – Alí

    28 de febrero a las 23:23

¿Por que llamariamos a cinclear y cinignore despues de leer
phy lieng

utilizar cin.ignore(1000,'\n') para borrar todos los caracteres de la anterior cin.get() en el búfer y elegirá detenerse cuando se encuentre con ‘\n’ o 1000 chars primero.

¿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