O tutorial C ++ da Google Code University costumava ter 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;
}
Qual é o significado de cin.clear()
e cin.ignore()
? Por que os parâmetros 10000
e \n
são necessários?
Respostas:
O
cin.clear()
apaga o sinalizador de errocin
(para que futuras operações de I / O funcionem corretamente) e, em seguida,cin.ignore(10000, '\n')
pula para a próxima nova linha (para ignorar qualquer outra coisa na mesma linha do não-número para que não cause outra falha de análise) . Ele pulará apenas até 10.000 caracteres, portanto, o código pressupõe que o usuário não colocará uma linha muito longa e inválida.fonte
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
.std::numeric_limits
, certifique-se de usar#include <limits>
.std::numeric_limits<std::streamsize>::max()
?Você entra no
if (!(cin >> input_var))
declaração se ocorrer um erro ao obter a entrada de cin. Se ocorrer um erro, um sinalizador de erro será definido e as tentativas futuras de obter dados falharão. É por isso que você precisa
cin.clear();
para se livrar do sinalizador de erro. Além disso, a entrada que falhou ficará no que presumo ser algum tipo de buffer. Quando você tentar obter a entrada novamente, ele lerá a mesma entrada no buffer e falhará novamente. É por isso que você precisa
cin.ignore(10000,'\n');
Ele retira 10.000 caracteres do buffer, mas para se encontrar uma nova linha (\ n). O 10000 é apenas um grande valor genérico.
fonte
Por que usamos:
1) cin.ignore
2) cin.clear
?
Simplesmente:
1) Para ignorar (extrair e descartar) valores que não queremos no fluxo
2) Para limpar o estado interno do fluxo. Depois de usar cin.clear, o estado interno é definido novamente como goodbit, o que significa que não há 'erros'.
Versão longa:
Se algo for colocado em 'stream' (cin), então deve ser retirado de lá. Por 'levado' queremos dizer 'usado', 'removido', 'extraído' do fluxo. O fluxo tem um fluxo. Os dados estão fluindo como água no fluxo. Você simplesmente não pode parar o fluxo de água;)
Olhe para o exemplo:
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
O que acontece se o usuário responder: "Arkadiusz Wlodarczyk" para a primeira pergunta?
Execute o programa para ver por si mesmo.
Você verá no console "Arkadiusz", mas o programa não pedirá 'idade'. Ele vai terminar imediatamente após a impressão de "Arkadiusz".
E "Wlodarczyk" não é mostrado. Parece que foi embora (?) *
O que aconteceu? ;-)
Porque há um espaço entre "Arkadiusz" e "Wlodarczyk".
O caractere de "espaço" entre o nome e o sobrenome é um sinal para o computador de que há duas variáveis esperando para serem extraídas no fluxo de 'entrada'.
O computador pensa que você está tentando enviar para a entrada mais de uma variável. Esse sinal de "espaço" é um sinal para ele interpretar dessa forma.
Portanto, o computador atribui "Arkadiusz" ao 'nome' (2) e, como você colocou mais de uma string no fluxo (entrada), o computador tentará atribuir o valor "Wlodarczyk" à variável 'idade' (!). O usuário não terá a chance de colocar nada no 'cin' na linha 6 porque essa instrução já foi executada (!). Por quê? Porque ainda havia algo em funcionamento. E como eu disse antes, o fluxo está em um fluxo, então tudo deve ser removido dele o mais rápido possível. E a possibilidade surgiu quando o computador viu a ciência da instrução;
O computador não sabe que você criou uma variável que armazena a idade de alguém (linha 4). 'idade' é apenas um rótulo. Pois a 'era' do computador também poderia ser chamada de: 'afsfasgfsagasggas' e seria o mesmo. Para ele, é apenas uma variável que tentará atribuir "Wlodarczyk" porque você ordenou / instruiu o computador a fazê-lo na linha (6).
É errado fazer isso, mas foi você quem fez isso! É sua culpa! Bem, talvez usuário, mas ainda ...
Tudo bem, tudo bem. Mas como consertar ?!
Vamos tentar brincar um pouco com esse exemplo antes de corrigi-lo adequadamente para aprender mais algumas coisas interessantes :-)
Prefiro fazer uma abordagem onde entendemos as coisas. Consertar algo sem saber como a gente fazia não dá satisfação, não acha? :)
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 :-)
Depois de invocar o código acima, você notará que o estado do seu stream (cin) é igual a 4 (linha 7). O que significa que seu estado interno não é mais igual a goodbit. Algo está bagunçado. É bastante óbvio, não é? Você tentou atribuir um valor de tipo de string ("Wlodarczyk") à variável de tipo int 'idade'. Tipos não correspondem. É hora de informar que algo está errado. E o computador faz isso mudando o estado interno do fluxo. É como: "Seu f ****-up cara, me conserta por favor. Eu te informo 'gentilmente' ;-)"
Você simplesmente não pode mais usar 'cin' (stream). Está preso. Como se você tivesse colocado grandes toras de madeira no riacho. Você deve consertá-lo antes de poder usá-lo. Os dados (água) não podem mais ser obtidos desse riacho (cin) porque a tora de madeira (estado interno) não permite que você faça isso.
Ah, então se houver um obstáculo (toras de madeira), podemos simplesmente removê-lo usando ferramentas feitas para isso?
Sim!
o estado interno de cin definido como 4 é como um alarme que está uivando e fazendo barulho.
cin.clear limpa o estado de volta ao normal (goodbit). É como se você tivesse vindo e silenciado o alarme. Você apenas adia. Você sabe que algo aconteceu, então você diz: "Tudo bem parar de fazer barulho, eu sei que algo já está errado, cale a boca (limpe)".
Tudo bem, vamos fazer isso! Vamos usar cin.clear ().
Chame o código abaixo usando "Arkadiusz Wlodarczyk" como primeira 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 :-)
Certamente podemos ver depois de executar o código acima que o estado é igual a goodbit.
Ótimo então o problema está resolvido?
Chame o código abaixo usando "Arkadiusz Wlodarczyk" como primeira 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 :-)
Mesmo que o estado seja definido como goodbit após a linha 9, o usuário não é questionado sobre "idade". O programa pára.
PORQUE?!
Oh cara ... Você acabou de disparar o alarme, que tal a tora de madeira dentro de uma água? * Volte para o texto onde falamos sobre "Wlodarczyk" como supostamente tinha sumido.
Você precisa remover "Wlodarczyk" aquele pedaço de madeira do riacho. Desligar os alarmes não resolve o problema. Você acabou de silenciá-lo e acha que o problema desapareceu? ;)
Então é hora de outra ferramenta:
cin.ignore pode ser comparado a um caminhão especial com cordas que vem e remove as toras de madeira que travaram o riacho. Isso elimina o problema que o usuário do programa criou.
Então, poderíamos usá-lo antes mesmo de fazer o alarme disparar?
Sim:
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;
O "Wlodarczyk" será removido antes de fazer o ruído na linha 7.
O que é 10000 e '\ n'?
Diz remover 10.000 caracteres (apenas no caso) até que '\ n' seja encontrado (ENTER). BTW, isso pode ser feito melhor usando numeric_limits, mas não é o tópico desta resposta.
Portanto, a principal causa do problema foi embora antes que o ruído fosse feito ...
Por que precisamos 'limpar' então?
E se alguém tivesse feito a pergunta 'dê-me sua idade' na linha 6, por exemplo: "vinte anos" em vez de escrever 20?
Tipos não correspondem novamente. O computador tenta atribuir string a int. E o alarme começa. Você nem mesmo tem chance de reagir em uma situação como essa. cin.ignore não o ajudará neste caso.
Portanto, devemos usar claro em casos como este:
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
Mas você deve limpar o estado 'apenas no caso'?
Claro que não.
Se algo der errado (cin >> age;), a instrução irá informá-lo sobre isso retornando false.
Portanto, podemos usar a instrução condicional para verificar se o usuário colocou o tipo errado no fluxo
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";
Tudo bem, então podemos corrigir nosso problema inicial como por exemplo:
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; }
Claro que isso pode ser melhorado, por exemplo, fazendo o que você fez em questão usando loop while.
BÔNUS:
Você pode estar se perguntando. E se eu quisesse que o nome e o sobrenome do usuário fossem na mesma linha? É mesmo possível usar cin se cin interpretar cada valor separado por "espaço" como uma variável diferente?
Claro, você pode fazer isso de duas maneiras:
1)
string name, surname; cout << "Give me your name and surname:"<< endl; cin >> name; cin >> surname; cout << "Hello, " << name << " " << surname << endl;
2) ou usando a função getline.
getline(cin, nameOfStringVariable);
e é assim que se faz:
string nameAndSurname; cout << "Give me your name and surname:"<< endl; getline(cin, nameAndSurname); cout << "Hello, " << nameAndSurname << endl;
A segunda opção pode sair pela culatra em caso de você usá-lo depois de usar 'cin' antes do getline.
Vamos dar uma olhada:
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;
Se você colocar "20" como idade, não será solicitado o nome e o sobrenome.
Mas se você fizer assim:
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
tudo está bem.
O QUE?!
Cada vez que você coloca algo na entrada (fluxo), você deixa no final um caractere branco que é ENTER ('\ n'). Você tem que inserir valores de alguma forma para o console. Portanto, deve acontecer se os dados vierem do usuário.
b) as características do cin são que ele ignora os espaços em branco, então quando você está lendo informações de cin, o caractere de nova linha '\ n' não importa. É ignorado.
a) a função getline obtém toda a linha até o caractere de nova linha ('\ n'), e quando o caractere de nova linha é a primeira coisa, a função getline obtém '\ n', e isso é tudo para obter. Você extrai o caractere de nova linha que foi deixado na transmissão pelo usuário que colocou "20" na transmissão na linha 3.
Portanto, para consertar é sempre invocar cin.ignore (); cada vez que você usar cin para obter qualquer valor, se algum dia for usar getline () dentro de seu programa.
Portanto, o código adequado seria:
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 os streams sejam mais claros para você.
Hah me silencie por favor! :-)
fonte
use
cin.ignore(1000,'\n')
para limpar todos os chars do anteriorcin.get()
no buffer e irá escolher parar quando encontrar '\ n' ou1000 chars
primeiro.fonte