Endlosschleife nach Buchstabeneingabe

IZZO

Mitglied
Moin,

ich bin neu bei C++ und habe ein kleines Programm zur Prozentrechnung geschrieben. Alles klappt wunderbar außer, dass bei einer Buchstabeneingabe dort wo man eigentlich nur 1,2,3,4 eingeben darf das Programm in eine Endlosschleife zu gehen scheint. Jedenfalls werden unendlich Sachen über die Konsole ausgegeben. Meiner Meinung nach habe ich durch die else-Funktion jedoch definiert was passieren soll wenn die Eingabe nicht 1,2,3 oder 4 ist. Bei Zahlen funktioniert die else-Funktion auch wunderbar. Wie verhindere ich also diese Endlosschleife? Kann ich nur bestimmte Datenwerte als Eingabe zulassen?

Vielen Dank!

C++:
#include <iostream>
using namespace std;

bool trueinput = true;
bool weitergehts = true;
void procedure();

int main() {

    while(weitergehts == true){
        procedure();
    while(trueinput == false){
            procedure();

    }
}
    return 0;
}

void procedure(){

    double zahlen[3];
    int eingabe_auswahl;

    cout << "Grundwert:   1\n";
    cout << "Prozentwert: 2\n";
    cout << "Prozentsatz: 3\n";
    cout << "Beenden:     4\n";

    cin >> eingabe_auswahl;

    if(eingabe_auswahl ==  1 || eingabe_auswahl == 2 || eingabe_auswahl == 3 || eingabe_auswahl == 4){

        switch(eingabe_auswahl){
        case 1: cout <<"Geben Sie den Prozentwert ein und bestätigen mit Enter. Verfahren Sie genauso mit dem Prozentsatz (in %).\n";
                cin >> zahlen[0];
                cin >> zahlen[1];
                zahlen[1] = zahlen[1] / 100;
                zahlen[2] = zahlen[0] / zahlen [1];
                cout << "Mit einem Prozentwert von " << zahlen[0] << " und einem Prozentsatz von " << zahlen[1] * 100
                     << "% ergibt sich ein Grundwert von " << zahlen[2] << ".\n\n";
                cin.sync();
                weitergehts = true;
                procedure();
        break;

        case 2: cout <<"Geben Sie den Grundwert ein und bestätigen mit Enter. Verfahren Sie genauso mit dem Prozentsatz (in %).\n";
                cin >> zahlen[0];
                cin >> zahlen[1];
                zahlen[1] = zahlen[1] / 100;
                zahlen[2] = zahlen[0] * zahlen [1];
                cout << "Mit einem Grundwert von " << zahlen[0] << " und einem Prozentsatz von " << zahlen[1] * 100
                     << "% ergibt sich ein Prozentwert von " << zahlen[2] << ".\n\n";
                cin.sync();
                weitergehts = true;
                procedure();
        break;

        case 3: cout <<"Geben Sie den Prozentwert ein und bestätigen mit Enter. Verfahren Sie genauso mit dem Grundwert.\n";
                cin >> zahlen[0];
                cin >> zahlen[1];
                zahlen[2] = zahlen[0] / zahlen [1];
                cout << "Mit einem Prozentwert von " << zahlen[0] << " und einem Grundwert von " << zahlen[1]
                     << " ergibt sich ein Prozentsatz von " << zahlen[2] * 100 << "%.\n\n";
                cin.sync();
                weitergehts = true;
                procedure();
        break;

        case 4: weitergehts = false;
        break;

    }

        trueinput = true;

    }


    else{
        cout << "Bitte geben Sie die Zahlen '1 für Grundwertberechnung', '2 für Prozentwertberechnung' "
                ", '3 für Prozentsatzberechnung' oder '4 zum Beenden' ein.\n";
        trueinput = false;
    }
}
 
Hallo

Zuerst zu deinem Problem. Das liegt hier:
C++:
while(weitergehts == true){
        procedure();
   while(trueinput == false){
            procedure();
Die 2. Schleife wird immer ausgeführt. Vor allem, weil cin.sync() nicht ausgeführt wird, wenn der Input nicht stimmte. Daher solltest du die Eingabe nach dem Einlesen immer synchronisieren.

Ein paar generelle Sachen: "using namespace" ist verpönt. Man schreibt normalerweise das std:: aus.
Globale Variablen sind ebenfalls "böse". Was du willst, kannst du gut mit einem boolschen Rückgabewert (anstatt void) machen.
Zudem ist deine Rekursion ein absolutes Todesurteil. Du rekursierst und iterierst gleichzeitig (procedure() innerhalb von procedure() aufzurufen ist nur selten sinnvoll, vor allem, wenn procedure() sowieso im while() geloopt wird).
Ebenfalls solltest du dein double nicht unbedingt mit int multiplizieren. Nimm besser 100.0 als 100 (auch wenn der Compiler automatisch zu double konvertieren wird).
Ah und noch was: Statt "\n" nutzt man in c++ eigentlich std::endl. Klar geht "\n" auch, aber konsequenterweise müsste man das C++-Äquivalent nutzen.

Oha. Viel Denglish hier :)

Gruss
cwriter
 
Mhh, also ich hab das ausprobiert, aber es hilft nicht. Ich mein else soll doch bei JEDER anderen Eingabe als 1,2,3 oder 4 ausgeführt werden. Also bei Zahlen UND Buchstaben. Bei Zahlen klappts ja auch nur bei Buchstaben nicht.....

Sonst Dankeschön für die anderen Tipps!
 
Hm. Ich arbeite eigentlich nie mit std::cin. Ich könnte mir aber vorstellen, dass dadurch, dass du in einen int lädst, auch nur ints zugelassen werden. Ersetze "int eingabe_auswahl" mal durch "char eingabe_auswahl". Eventuell musst du dann auf == "1" statt == 1 prüfen.

Gruss
cwriter
 
Dann funktioniert die If-Funktion nicht mehr (ISO C++ forbids comparison between pointer and integer [-fpermissive]) und der switch auch nicht (case label does not reduce to an integer constant)
 
Vielen Vielen Dank, jetzt läufts. Das komische ist, dass Eclipse mir nen Fehler angestrichen hat. Wenn man das Prog dann kompiliert hat verschwand der und alles lief! Also vielen Dank!
 
Hallo IZZO,
wieso hast du denn einen neuen Thread geöffnet, du hast uns doch bereits nach einer Lösung für dieses Problem gefragt?
https://www.tutorials.de/threads/programm-reagiert-bei-buchstabeneingabe-komisch.400657/

Sehr gut finde ich, dass du die Abfrage in eine Funktion gekapselt, und auf die Verwendung von goto verzichtet hast ;).
Wenn du nun noch an die globalen Variablen denkst (also diese nicht zu verwenden, oder im einfachsten Fall in deine main() zu packen) und daran, dass using namespace std; nicht global zu verwenden, befindest du dich auf einem guten Weg :).
Ich glaube ich habe dir das letzte Mal nicht richtig erklärt was das Problem ist, deshalb spielen wir das jetzt hier kurz durch:

Im Falle einer gültigen Eingabe:
Du führst
C++:
cin >> eingabe_auswahl;
aus, und prüfst mittels
C++:
if(eingabe_auswahl ==  1 || eingabe_auswahl == 2 || eingabe_auswahl == 3 || eingabe_auswahl == 4){
ob die Eingabe eine bestimmte Zahl ist. Das wichtige hieran ist, dass eingabe_auswahl ein Integer ist. Wenn du also z.B. die Zahl "3" (ohne Anführungszeichen) eingibst, und mit Enter bestätigst, dann befindet sich im Eingabepuffer der String: "3\n" (ohne Anführungszeichen). Das cin liest nun die Eingabe bis zum ersten Whitespace ein und versucht daraus einen Integer - also eine Ganzzahl - zu parsen. Bei "3" geht das, also wird das Ergebnis in eingabe_auswahl geschrieben (= 3). Die if-Abfrage liefert daraufhin WAHR und der Codeblock des if wird ausgeführt (und der des else verworfen). Das Programm läuft regulär weiter.

Im Falle einer ungültigen Eingabe:
Nehmen wir an, du gibst stattdessen "a" (ohne Anführungszeichen) ein. Wieder liest das cin alles bis zum Whitespace ein, also wird "a\n" zu "a". Nun passierts: Das Parsen von "a" geht schief, weil "a" keine Ganzzahl, noch nicht einmal irgendeine Zahl, ist. Was jetzt passiert ist meiner Erfahrung nach eine Frage des Compilers, meistens wird glaube ich der ASCII-Wert des Zeichens in den Integer eingetragen, also in dem Fall wäre eingabe_auswahl = 97. Aber: Da die Eingabe nicht zu dem angegebenen Typ (int) passt, wird der Eingabepuffer nicht geleert. Im Eingabepuffer bleibt also "a\n" stehen, und wenn du das nächste Mal cin aufrufst, wird dieser String automatisch daran weitergeleitet (zum Parsen). Da eingabe_auswahl nicht zwischen 0 und 4 liegt, ist deine if-Bedingung FALSE und es wird der else-Codeblock ausgeführt. In diesem Block gibst du einen Warnhinweis aus, und sorgst mittels
C++:
trueinput = false;
dafür, dass unmittelbar die nächste Eingabe abgefragt wird.
ABER: Du kannst gar nichts eingeben, weil ja im Eingabepuffer noch der String "a\n" vorhanden ist, und das cin sofort wieder versucht diesen zu einem Integer zu parsen (was ja nicht geht). Du befindest dich also in einer Endlosschleife, die du nur duch Strg+C unterbrechen kannst, und die dir ansonsten unendlich oft einen Warnhinweis ausspuckt.

Die Lösung:
Die Lösung ist relativ simpel. Wenn du eine fehlerhafte Eingabe erhältst, prüfst du zuerst mittels cin.fail(), ob im Eingabepuffer noch Zeugs steht, dass beim nächsten Aufruf von cin unmittelbar weitergeleitet wird. Falls ja, setzt du mittels cin.clear() die Fehlerflags von cin zurück (damit ist cin.fail() jetzt nicht mehr TRUE). Anschließend leerst du noch den Eingabepuffer, indem du mittels
C++:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
alle Zeichen die noch im Puffer stehen verwirfst (dieses numeric_limits benutze ich, damit dieser Aufruf für Eingaben beliebiger länge funktionieren, in Wahrheit wird aber nur alles bis zum nächsten "\n" verworfen).
Insgesamt ergibt sich also folgender Block:
C++:
if (std::cin.fail()) { // check if stream is good before repeating
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Wenn du den vor dem erneuten Aufrufen von procedure() einfügst, kannst du auch nach falschen Eingaben erneut etwas eingeben ;).
Ach ja: Du musst noch ein
C++:
#include <limits> // for std::numeric_limits
hinzufügen, damit das ganze funktioniert.

Hoffe ich konnte dieses Mal helfen.
Gruß Technipion

PS: Es gibt natürlich auch andere Möglichkeiten wie man das mit dem Eingabepuffer lösen kann, aber ich finde diese Methode ist die beste ;)
 
Dankeschön für die ausführliche Erklärung! Und sorry für den Doppelpost, aber das Problem wurde beim letzten Post nicht gelöst (bzw. ich war nicht fähig es zu lösen). Nun klappt´s ja alles dank euch!
 
Zurück