Lekcja 15. Pierwsze zadanie domowe.
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!

Lekcja 15. Pierwsze zadanie domowe.

AutorWiadomość
Temat założony przez niniejszego użytkownika
Lekcja 15. Pierwsze zadanie domowe.
» 2017-01-13 20:19:59
Cześć, w lekcji 15 jest zadanie domowe o treści:
Napisz program, który wczyta liczbę, a następnie wypisze ją na ekranie. Zabezpiecz przed wczytaniem nieprawidłowej liczby. Wykorzystaj do tego celu wiedzę z jednego z poprzednich rozdziałów. Sposób działania programu:
1. Podaj liczbę
2. Jeżeli błąd, wróć do kroku 1.
3. Wypisz liczbę, która została podana.

Mój kod wygląda następująco:

C/C++
#include <iostream>
#include <cstdio>
using namespace std;

main()
{
    int PodanaLiczba;
    bool CzyLiczba;
   
    do
    {
        cout << "Podaj liczbe" << endl;
        // cin.clear();
        // cin.sync();
        cin >> PodanaLiczba;
        CzyLiczba = cin.fail();
       
       
    } while( CzyLiczba );
   
    cout << "Podana liczba = " << PodanaLiczba;
   
    return 0;
   
}

Jeżeli usunę komentarz przy linijkach:
C/C++
cin.clear();
cin.sync();

program spełnia swoją funkcję, jeśli podamy liczbę, wypiszę ją na ekranie, jeśli inny znak to każe nam podać jeszcze raz. Natomiast mnie interesuje dlaczego jeżeli pominiemy te dwie linijki np. komentując je to program:
Po wpisaniu liczby/cyfry wyświetli ją na ekranie, ale po wpisaniu złego znaku np. litery a program w koło będzie wyświetlał "Podaj liczbe" i nic już się nie da zrobić oprócz wyłączenia programu.
Czy to dlatego, że dzięki cin.clear() czyścimy flagę błędu cin.fail? Nie mogę zrozumieć co się dzieję, że dzięki tym dwóm linijkom program działa jak należy.

EDIT: Rzuciłem po herbacie jeszcze okiem na ten kod. Czy to dlatego, ze jeśli nie ma cin.clear() oraz cin.sync() to program za zmienną PodanaLiczba przyjmuję wartość podaną za pierwszym razem (np litera 'a') i się zapętla, bo warunek while ( CzyLiczba ); będzie ciągle przyjmował wartość 1 z tego powodu?
P-156388
» 2017-01-13 21:23:52
ale po wpisaniu złego znaku np. litery a program w koło będzie wyświetlał "Podaj liczbe"
To dlatego, że wczytanie danych do zmiennej nie powidło się i na strumieniu ustawiona została flaga failbit,
która blokuje użycie operatora
<<
. (oczywiście nieprawidłowe dane wciąż są w buforze strumienia).
Dlatego należy użyć metody
std::cin.clear();
 do ustawienia flag w stan prawidłowy (wyzerowanie)
oraz metody
std::cin.sync();
 do pozbycia się nieprawidłowych danych przed kolejnym wczytywaniem.

http://www.cplusplus.com​/reference/ios/ios/good/

Proponuję poznać bliżej
std::cin.ignore()
 zamiast
std::cin.sync()
.
P-156394
Temat założony przez niniejszego użytkownika
» 2017-01-14 11:21:55
Dziekuje, czyli moje podejrzenia były słuszne (nie wiem czy ktoś zrozumiał mój wywód). Zapoznam się chętnie z podaną funkcja.

EDIT:
Dlaczego:
C/C++
cin.clear();
cin.sync();

spełnia swoją funkcję i program podany w 1 poście działa, a już użycie tego samego na odwrót czyli:

C/C++
cin.sync();
cin.clear();
nie działa i powoduje, że po wpisaniu litery zamiast cyfry program wyświetla w koło to samo?
P-156411
» 2017-01-14 20:52:56
a już użycie tego samego na odwrót
nie działa i powoduje, że po wpisaniu litery zamiast cyfry program wyświetla w koło to samo?

Pomyśl przez chwilę, jaka kolejność działań jest prawidłowa?

  • Odblokowanie strumienia.
  • Opróżnienie strumienia z nieprawidłowych danych.
czy
  • Próba opróżnienia zablokowanego strumienia (niemożliwa).
  • Odblokowanie strumienia.

Pamiętaj, że kod programu C++ wykonuje się od góry do dołu.

P-156440
Temat założony przez niniejszego użytkownika
» 2017-01-14 22:05:24
cin.clear();    Odblokowanie strumienia.
cin.sync();    Opróżnienie strumienia z nieprawidłowych danych.

Za kursem:
"Jeśli chcemy mieć większą kontrolę nad strumieniem wejściowym to powinniśmy czyścić jego zawartość przed każdym wczytaniem danych. Aby to zrobić musimy wywołać dwie metody strumienia std::cin. Pierwszą z nich jest std::cin.clear(), która czyści flagi błędu. Drugą metodą jest std::cin.sync(), która czyści bufor strumienia."

Ja z tego rozumiem,że cin.clear(); czyści flagę błędu i nie mamy wtedy wartości przypisanej w cin.good(); (bo ją wyczyściliśmy). A z tego co piszesz cin.clear(); odblokowuje strumień? co niestety nie wiem co oznacza,jak ten strumień był zablokowany?.

Rozumiem, że w takim przypadku:

C/C++
int Jakaszmienna;

std::cin >> Jakaszmienna;
Zadeklarowaliśmy zmienną int, więc program będzie czekał na jakąś cyfrę/liczbę. Jeśli wpiszemy np. "123 a 45" i wciśniemy enter to zmienna Jakaszmienna przyjmie wartość 123, a w strumieniu bufora zostanie nam "a 45" i jak program będzie chciał pobrać jakieś dane poprzez cin ponownie to jeżeli nie wyczyścimy tego bufora to program pobierze automatycznie "a", bo to była kolejna rzecz którą miał w buforze strumienia?


Proponujesz zapoznanie się z cin.ignore(); taki zapis cin.ignore( 1000, '\n' ); oznacza, że w analogicznej do powyższej sytuacji

C/C++
int Jakaszmienna;

std::cin >> Jakaszmienna;
cin.clear();
cin.ignore( 1000, '\n' )
Tutaj po wpisaniu "123 a 45" program do zmiennej Jakaszmienna przypisze wartość 123, a resztę "a 45" zignoruję tak? Tylko dlaczego ignorowanie kończy wtedy, gdy napotka \n? To dlatego, że kiedy wpisujemy w programie jakieś dane i zatwierdzamy ENTER'em to program nasz enter zapisuje do buforu jako \n? Czyli dzięki temu ignoruje wszystko od "123" aż do czasu kiedy wciśniemy enter? Jeśli nie kończyłoby ignorowania na \n to czy (upraszczając) program gdyby dalej w kodzie miałby wczytać coś z bufora to czy wszystko co wprowadzimy w ten bufor byłoby zignorowane(do 1000 znaków), bo nie zaznaczyliśmy, że przy enterze ma się kończyć ignorowanie? I ostatnie pytanie: Czemu dzięki temu nie ignorujemy też pierwszego ciągu znaków czyli "123", ponieważ funkcją cin tak jakby wciągnęliśmy z bufora te "123" i jedynie to co było dalej wpisane jest w buforze?

Jeżeli to za dużo pytań, lub pytania zdają się zbyt chaotyczne to przepraszam i proszę o informację, zredaguje je trochę inaczej.
P-156446
» 2017-01-14 23:53:16
czyści flagę błędu
 czyli ustawia flagi strumienia na wartości poprawne - odblokowuje strumień do pracy.
nie mamy wtedy wartości przypisanej w cin.good();
 - metoda
std::cin.good()
 zwraca wartość logiczną,
true
 lub
false
, więc zawsze coś zwraca. Do metody nie można nic przypisać.

Drugą metodą jest std::cin.sync(), która czyści bufor strumienia."
W większości przypadków to działa, ale nie do tego służy ta metoda.
Oczyszczenia bufora strumienia jest efektem ubocznym i nie wszędzie działa (linux, MS Visual od 2015),
więc można się zdziwić, że program się zapętla, a przecież oczyszczamy strumień.

Przykład:
C/C++
int a;
std::cin >> a; // podajemy na wejście standarodow 123 a 45 i zatwierdzamy enterem (zawartość bufora "123 a 45\n")
// operator >> pobiera wszystkie pasujące dane z bufora strumienia do zmiennej "a"
// i w buforze pozostaje " a 45"
std::cin >> a; // ponowne wczytanie: operator >> pomija znak spacji (tak ma, chyba że to wyłączymy)
// i próbuje wczytać znak 'a' do zmiennej typu int.
// wczytanie powoduje zablokowanie operatora strumienia >> (jest ignorowany).
std::cin.clear(); // zeruje flagi strumienia (odblokowuje go do działania).
std::cin.ignore( 1000, '\n' ); // odrzuca z bufora strumienia do 1000 znaków lub napotkania znaku nowej linii '\n'
// ale jeśli danych będzie więcej niż 1000 znaków, bufor nie opróżniony, bo zostaną znaki od 1001 w górę.
// Jeszcze jeden myk: jeśli bufor będzie pusty, metoda std::cin.ignore będzie oczekiwać na wprowadzenie jakiegoś znaku.

C/C++
std::cin.ignore < std::numeric_limits < std::streamsize >::max(), '\n' ); // wyczyści wszystko
std::iostream::ignore

P-156448
Temat założony przez niniejszego użytkownika
» 2017-01-15 16:02:27
Dziękuję bardzo za rozjaśnienie sytuacji. Ostatnie pytanie dotyczące tej sprawy: jeżeli wprowadzamy dane i zatwierdzamy enterem to w buforze znak ENTER zostanie zapisany jako \n, dlatego ignorujemy aż do pojawienia się \n, żeby nie wywołać takiej sytuacji, gdy wszystkie dane wprowadzone są ignorowane,aż nie zużyjemy 1000 znaków?
Czyli łopatologicznie:
C/C++
int a;

std::cin >> a;
std::cin.clear(); //Wprowadzamy: "123 a 45" zatwierdzając enterem i program widzi to jako: "123 a 45\n" i zostanie w buforze " a 45\n"
std::cin.ignore( 1000, '\n' ); //dzięki tej metodzie przy np. zapętleniu program, jeżeli wróci do punktu cin >> a; to w buforze zostaje " a 45\n", ale ignorujemy znaki " a 45\n", ignorować kończymy przy \n i dzięki temu wszystko co użytkownik wpiszę po wcześniejszym wciśnięciu entera będzie mogło znowu zostać wczytane, a nie zignorowane.
Wydaję mi się, że dzięki Tobie dobrze to zrozumiałem, jak na powyższym przykładzie. Dziękuję.

Próbując w praktyce nauczyć się działania cin.ignore( 1000, '\n'); natrafiłem na taki problem:
C/C++
#include <iostream>
using namespace std;

int main()
{
    int PodanaLiczba;
    bool CzyLiczba;
    do
    {
        cout << "Podaj liczbe " << endl;
        cin.clear();
        cin.ignore( 1000, '\n' );
        cin >> PodanaLiczba;
        CzyLiczba = cin.good();
    } while( CzyLiczba == 0 );
   
    cout << "Podana liczba = " << PodanaLiczba;
   
    return 0;
}
program działa następująco w konsoli:

Podaj liczbę
a //tutaj wpisuję 'a' i zatwierdzam enterem
a //tutaj robię to samo, bo nic się nie dzieję za 1 razem
Podaj liczbę // program orientuje się, że a to nie liczba i wymusza podanie jeszcze raz( tylko dlaczego za 1 razem się nie orientuje?)
12 // wpisuję 12, czyli liczbę
Podana liczba = 12 // program działa, wypisuję liczbę
analogicznie sytuacja wygląda gdy od razu podaję zmienną zgodną z typem int czyli liczbę, wpisuję ją raz i wysyłam enterem, program nie reaguję, wpisuję drugi raz i program działa.
jeżeli linijkę
cin.ignore( 1000, '\n' );
 zamienię na
cin.sync();
 to program działa tak jak zamierzyłem, czyli nie ma problemu z tym, że za pierwszym razem muszę wprowadzić dwa razy dane, żeby je pobrało.
P-156458
» 2017-01-15 17:30:37
Jak wspomniałem
C/C++
// Jeszcze jeden myk: jeśli bufor będzie pusty, metoda std::cin.ignore będzie oczekiwać na wprowadzenie jakiegoś znaku.
I właśnie taki przypadek jest w twoim kodzie.
C/C++
do
{
    cout << "Podaj liczbe " << endl;
    cin.clear();
    cin.ignore( 1000, '\n' ); // bufor jest pusty, więc musimy coś wprowadzić i natychmiast jest to usuwane
    cin >> PodanaLiczba; // i dopiero teraz przechodzi do następnej linii, czyli właściwego wczytywania
    CzyLiczba = cin.good();
} while( CzyLiczba == 0 );
Sprawdzaj, czy strumień jest w stanie poprawny i jeśli nie jest oczyszczaj go.

Przykład:
C/C++
#include <iostream>

int main()
{
    int a;
    do {
        if( !std::cin ) { // równoważne !std::cin.good()
            std::cin.clear();
            std::cin.ignore( std::numeric_limits < std::streamsize >::max(), '\n' );
        }
        std::cin >> a;
    } while( !std::cin );
   
    std::cout << a << '\n';
}

Przeczytaj o jeszcze innych sposobach zabezpieczenia się przed literkami:
https://4programmers.net/C/FAQ​/Zabezpieczenie_przed_wpisywaniem_liter
P-156459
« 1 » 2
 Strona 1 z 2Następna strona