[C++] bezpieczna próba wczytania liczby z konsoli

Awatar użytkownika
alchem
Użytkownik
Użytkownik
Posty: 252
Rejestracja: 10 cze 2014, o 19:10
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 83 razy
Pomógł: 5 razy

[C++] bezpieczna próba wczytania liczby z konsoli

Post autor: alchem »

Mam takie zadanie:
Napisz funkcję, która podejmie bezpieczną próbę wczytania liczby całkowitej z konsoli. W przypadku, kiedy użytkownik wpisze coś, co nie jest liczbą, funkcja poinformuje o niepowodzeniu i ponowi próbę. Kiedy wreszcie użytkownik poda liczbę, funkcja zwróci jej wartość.
Mam 2 pytania, próbuje to zrobić tak:

Kod: Zaznacz cały

#include <iostream>
#include <cstdlib>
#include <sstream>
#include <fstream>

using namespace std;

int main()
{
    int pomocnicza_zmienna = 0;
    while(pomocnicza_zmienna = 0);
    {
        int liczba;
        std::cout <<"Podaj liczbe: ";
        if(!(std::cin >> liczba))
        {
            std::cout << "Nie podales liczby!";
             pomocnicza_zmienna = 0;
        }
        else
        {
            std::cout << liczba;
            pomocnicza_zmienna = 1;
        }

    }


    return EXIT_SUCCESS;
}
Tylko nie wiem dlaczego funkcja się nie zapętla, nawet jak nie wpisze liczby, to pokazuje się komunikat o tym i program kończy działanie, nie wiem dlaczego. I tak wgl jest to dopuszczalny sposób na 'bezpieczną próbę wczytywania"?
Na wykładzie mieliśmy coś z

Kod: Zaznacz cały

std::istringstream
i

Kod: Zaznacz cały

std::ostringstream

ale nie za bardzo wiem jak zrobić to sposobem z wyrażeniami wyżej.
Awatar użytkownika
Dasio11
Moderator
Moderator
Posty: 10218
Rejestracja: 21 kwie 2009, o 19:04
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 40 razy
Pomógł: 2361 razy

[C++] bezpieczna próba wczytania liczby z konsoli

Post autor: Dasio11 »

Kod: Zaznacz cały

[...]
    int pomocnicza_zmienna = 0;
    while(pomocnicza_zmienna = 0);
    {
        int liczba;
        std::cout <<"Podaj liczbe: ";
        [...]
    }
[...]
W tym kawałku są 2 błędy.

Po pierwsze, wewnątrz instrukcji while jest przypisanie = zamiast porównania ==, więc wartość 0 wpisze się do zmiennej pomocnicza_zmienna, a następnie zostanie potraktowana jako fałsz, co zakończy pętlę while bez jej wykonywania.

Po drugie, po instrukcji while jest średnik, więc ciało tej pętli jest puste.

W efekcie instrukcja while rozpoznaje fałsz, więc pomija wykonanie pustego ciała pętli. Cały następujący blok instrukcji jest traktowany bezwarunkowo, więc wykonuje się jednokrotnie, po czym program kończy działanie.


W kwestii działania programu:
- program w zamierzeniu może wielokrotnie wypisać linijkę tekstu, ale nie wypisuje znaku końca linii
, więc tekst będzie nieelegancko wyglądał; sugeruję zamienić std::cout << "Nie podales liczby!"; na std::cout << "Nie podales liczby!
";
;
- nie trzeba zerować zmiennej pomocnicza_zmienna za każdym razem, bo skoro jesteśmy w pętli, to znaczy że jest zerowa;
- w przypadku wpisania ciągu znaków, który nie jest liczbą, instrukcja std::cin >> liczba zwróci fałsz (co poprawnie sprawdzasz), ale nie wyjmie tego ciągu z bufora wejściowego, więc następne std::cin otrzyma ten sam ciąg znaków (w efekcie powodując zapętlenie programu); nie wiem dokładnie, jak powinno się to rozwiązać, ale chyba trzeba użyć funkcji std::cin.ignore().
Awatar użytkownika
alchem
Użytkownik
Użytkownik
Posty: 252
Rejestracja: 10 cze 2014, o 19:10
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 83 razy
Pomógł: 5 razy

[C++] bezpieczna próba wczytania liczby z konsoli

Post autor: alchem »

Aż się dziwie że sam tych błędów nie zauważyłem, no ale trudno ;d

Kod: Zaznacz cały

#include <iostream>
#include <cstdlib>
#include <sstream>
#include <fstream>

using namespace std;

int main()
{
    int pomocnicza_zmienna = 0;
    while(pomocnicza_zmienna == 0)
    {
        int liczba;
        std::cout <<"Podaj liczbe: ";
        if(!(std::cin >> liczba))
        {
            std::cin.clear();
            std::cin.sync();
            std::cout << "Nie podales liczby!"<<std::endl;

        }
        else
        {
            std::cout << liczba;
            pomocnicza_zmienna = 1;
        }

    }


    return EXIT_SUCCESS;
}

Trzeba było użyć

Kod: Zaznacz cały

std::cin.clear() 
( czyści flagi błędu) i

Kod: Zaznacz cały

std::cin.sync() 
(która czysci bufor) i śmiga.

Wie ktoś jak mogę to zrobić inaczej?
Wiem że mam użyć getlina oraz std::ostringstream lub std::istringstream, ale z tego co mi się wydaje jeśli już to to drugie.

W notatkach mam taki kawałek kodu, który mało mi mówi:

Kod: Zaznacz cały

std::istringstream wyraz("17");
int k = 0;
wyraz >> k;
if(wyraz) // no i tutaj chyba sprawdzam czy się udało poprawnie wczytać 

Ma ktoś jakiś pomysł?
ODPOWIEDZ