Dlaczego używamy:
1) cin.ignore
2) cin. Clear
?
Po prostu:
1) Aby zignorować (wyodrębnić i odrzucić) wartości, których nie chcemy w strumieniu
2) Aby wyczyścić wewnętrzny stan strumienia. Po użyciu cin.clear stan wewnętrzny jest ponownie ustawiany na goodbit, co oznacza, że nie ma żadnych „błędów”.
Długa wersja:
Jeśli coś jest umieszczane w „strumieniu” (cin), to musi być stamtąd zabrane. Przez „wzięty” rozumiemy „używany”, „usunięty”, „wyodrębniony” ze strumienia. Strumień ma przepływ. Dane płyną na cin jak woda w strumieniu. Po prostu nie można zatrzymać przepływu wody;)
Spójrz na przykład:
string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
Co się stanie, jeśli użytkownik odpowie: „Arkadiusz Włodarczyk” na pierwsze pytanie?
Uruchom program i przekonaj się sam.
Na konsoli zobaczysz „Arkadiusza”, ale program nie zapyta Cię o „wiek”. Po prostu skończy się zaraz po wydrukowaniu "Arkadiusza".
A "Włodarczyk" nie jest pokazywany. Wygląda na to, że zniknął (?) *
Co się stało? ;-)
Bo między "Arkadiuszem" a "Włodarczykiem" jest przestrzeń.
Znak "spacji" między imieniem i nazwiskiem jest dla komputera znakiem, że istnieją dwie zmienne oczekujące na wyodrębnienie ze strumienia „wejściowego”.
Komputer myśli, że chcesz wysłać do wejścia więcej niż jedną zmienną. Ten znak „spacji” jest dla niego znakiem, aby interpretował to w ten sposób.
Czyli komputer przypisuje „Arkadiusz” do „imienia” (2) i ponieważ umieścisz więcej niż jeden ciąg na strumieniu (wejściu), komputer będzie próbował przypisać wartość „Włodarczyk” zmiennej „wiek” (!). Użytkownik nie będzie miał szansy wstawić niczego do 'cin' w linii 6, ponieważ ta instrukcja została już wykonana (!). Czemu? Ponieważ wciąż coś zostało w strumieniu. I jak powiedziałem wcześniej, strumień płynie, więc wszystko trzeba z niego jak najszybciej usunąć. I taka możliwość pojawiła się, gdy komputer zobaczył instrukcję cin >> wiek;
Komputer nie wie, że utworzyłeś zmienną przechowującą wiek kogoś (wiersz 4). „wiek” to tylko etykieta. Dla komputerów „wiek” można by równie dobrze nazwać: „afsfasgfsagasggas” i byłoby to to samo. Dla niego to tylko zmienna, do której będzie próbował przypisać "Włodarczyk", ponieważ w linii (6) kazałeś / nakazałeś komputerowi zrobić to.
To źle, ale hej, to ty to zrobiłeś! To Twoja wina! Cóż, może użytkownik, ale nadal ...
W porządku, w porządku. Ale jak to naprawić ?!
Spróbujmy trochę pobawić się tym przykładem, zanim naprawimy go poprawnie, aby dowiedzieć się kilku ciekawszych rzeczy :-)
Wolę przyjąć podejście, w którym rozumiemy rzeczy. Naprawianie czegoś bez wiedzy, jak to zrobiliśmy, nie daje satysfakcji, nie sądzisz? :)
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();
Po wywołaniu powyższego kodu zauważysz, że stan twojego strumienia (cin) jest równy 4 (linia 7). Co oznacza, że jego stan wewnętrzny nie jest już równy goodbit. Coś jest nie w porządku. To całkiem oczywiste, prawda? Próbowałeś przypisać wartość typu string („Wlodarczyk”) do zmiennej typu int „age”. Typy nie pasują. Czas poinformować, że coś jest nie tak. A komputer robi to poprzez zmianę wewnętrznego stanu strumienia. To jest tak: „Ty jebany człowieku, napraw mnie proszę. Informuję Cię 'uprzejmie' ;-)”
Po prostu nie możesz już używać „cin” (stream). Utknęło. Tak jakbyś umieścił duże kłody drewna na strumieniu wody. Musisz to naprawić, zanim będziesz mógł z niego korzystać. Dane (woda) nie mogą być już pobierane z tego strumienia (cin), ponieważ log drewna (stan wewnętrzny) nie pozwala na to.
Och, więc jeśli jest przeszkoda (kłody drewna), możemy ją po prostu usunąć za pomocą narzędzi, które są do tego przeznaczone?
Tak!
stan wewnętrzny cin ustawiony na 4 jest jak alarm, który wyje i hałasuje.
cin.clear czyści stan z powrotem do normalnego (goodbit). To tak, jakbyś przyszedł i uciszył alarm. Po prostu to odkładasz. Wiesz, że coś się wydarzyło, więc mówisz: „Można przestać hałasować, wiem, że już coś jest nie tak, zamknij się (wyczyść)”.
W porządku, zróbmy to! Użyjmy cin.clear ().
Wywołaj poniższy kod, używając "Arkadiusz Włodarczyk" jako pierwszego wejścia:
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;
Z pewnością możemy zobaczyć po wykonaniu powyższego kodu, że stan jest równy goodbit.
Świetnie, więc problem został rozwiązany?
Wywołaj poniższy kod, używając "Arkadiusz Włodarczyk" jako pierwszego wejścia:
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;
Nawet jeśli stan jest ustawiony na goodbit po linii 9, użytkownik nie jest pytany o „wiek”. Program się zatrzymuje.
CZEMU?!
O rany ... Właśnie odłożyłeś alarm, a co z kłodą w wodzie? * Wróć do tekstu, w którym rozmawialiśmy o "Włodarczyku", jak to podobno zniknęło.
Trzeba usunąć "Włodarczyk" ten kawałek drewna ze strumienia. Wyłączenie alarmów w ogóle nie rozwiązuje problemu. Właśnie to uciszyłeś i myślisz, że problem zniknął? ;)
Czas więc na kolejne narzędzie:
cin.ignore można porównać do specjalnej ciężarówki z linami, które przyjeżdżają i usuwają kłody drewna, które utknęły w strumieniu. Rozwiązuje problem utworzony przez użytkownika programu.
Więc czy możemy go użyć jeszcze przed uruchomieniem alarmu?
Tak:
string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n');
int age;
cout << "Give me your age:" << endl;
cin >> age;
"Włodarczyk" zostanie usunięty przed hałasem w linii 7.
Co to jest 10000 i „\ n”?
Mówi, że usuń 10000 znaków (na wszelki wypadek), dopóki '\ n' nie zostanie spełniony (ENTER). BTW Można to zrobić lepiej, używając numeric_limits, ale nie jest to tematem tej odpowiedzi.
Więc główna przyczyna problemu zniknęła, zanim powstał hałas ...
Dlaczego więc potrzebujemy „jasności”?
A co by było, gdyby ktoś zapytał w linii 6 o „podaj mi swój wiek”, na przykład: „dwadzieścia lat” zamiast pisać 20?
Typy nie pasują ponownie. Komputer próbuje przypisać ciąg do int. I zaczyna się alarm. Nie masz nawet szansy zareagować na taką sytuację. cin.ignore nie pomoże Ci w takim przypadku.
Musimy więc użyć clear w takim przypadku:
string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n');
int age;
cout << "Give me your age:" << endl;
cin >> age;
cin.clear();
cin.ignore(10000, '\n');
Ale czy powinieneś wyczyścić stan „na wszelki wypadek”?
Oczywiście nie.
Jeśli coś pójdzie nie tak (cin >> wiek;) instrukcja poinformuje Cię o tym zwracając false.
Możemy więc użyć instrukcji warunkowej, aby sprawdzić, czy użytkownik umieścił w strumieniu niewłaściwy typ
int age;
if (cin >> age)
cout << "You put integer";
else
cout << "You bad boy! it was supposed to be int";
W porządku, więc możemy rozwiązać nasz początkowy problem, na przykład ten:
string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n');
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');
cout << "Give me your age name as string I dare you";
cin >> age;
}
Oczywiście można to poprawić, na przykład robiąc to, co robiłeś, używając pętli while.
PREMIA:
Możesz się zastanawiać. A co jeśli chciałbym otrzymać imię i nazwisko w tej samej linii od użytkownika? Czy jest w ogóle możliwe użycie cin, jeśli cin interpretuje każdą wartość oddzieloną „spacją” jako inną zmienną?
Jasne, możesz to zrobić na dwa sposoby:
1)
string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;
cout << "Hello, " << name << " " << surname << endl;
2) lub używając funkcji getline.
getline(cin, nameOfStringVariable);
i tak to się robi:
string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);
cout << "Hello, " << nameAndSurname << endl;
Druga opcja może przynieść odwrotny skutek, jeśli użyjesz jej po użyciu „cin” przed getline.
Sprawdźmy to:
za)
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;
Jeśli jako wiek wpiszesz „20”, nie zostaniesz poproszony o podanie imienia i nazwiska.
Ale jeśli zrobisz to w ten sposób:
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
wszystko w porządku.
CO?!
Za każdym razem, gdy umieszczasz coś na wejściu (strumieniu), zostawiasz na końcu biały znak, którym jest ENTER ('\ n'). Musisz jakoś wprowadzić wartości do konsoli. Musi się więc zdarzyć, jeśli dane pochodzą od użytkownika.
b) cechą cin jest to, że ignoruje białe znaki, więc kiedy czytasz informacje z cin, znak nowej linii „\ n” nie ma znaczenia. Jest ignorowany.
a) Funkcja getline pobiera całą linię aż do znaku nowej linii ('\ n'), a kiedy znak nowej linii jest pierwszą rzeczą, którą funkcja getline otrzymuje '\ n', i to wszystko do uzyskania. Wydobywasz znak nowej linii, który pozostawił w strumieniu przez użytkownika, który umieścił „20” w strumieniu w linii 3.
Aby to naprawić, należy zawsze wywoływać cin.ignore (); za każdym razem, gdy używasz cin do uzyskania jakiejkolwiek wartości, jeśli kiedykolwiek zamierzasz używać getline () w swoim programie.
Zatem właściwy kod to:
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cin.ignore();
cout << "Your age is" << age << endl;
string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);
cout << "Hello, " << nameAndSurname << endl;
Mam nadzieję, że strumienie są bardziej przejrzyste, wiesz.
Hah, ucisz mnie, proszę! :-)