[C++] einlesen von float mit cin

Cobinja

Lt. Junior Grade
Registriert
Juli 2006
Beiträge
454
Moin

Ich hab mal 'ne Frage zu C++

Ich muss einen float per cin einlesen. Bisher hab ich das folgendermassen gemacht:
Code:
float getInput(string name) {
  float tmpInputFloat = 0; // temp variable for user input
  do {
    cout << name << ": ";
    cin >> tmpInputFloat;
    // empty the input buffer
    getchar();
    // the user didnt't enter a number greater than zero or
    // didn't enter any number
    if (tmpInputFloat == 0) {
      cin.clear();
      cin.sync();
      // hint for the user
      cout << "Okay, naechster Versuch...\n";
    }
  // repeat it until the user enters a usable number geater than zero
  } while (tmpInputFloat <= 0);
  return tmpInputFloat;
Da habe ich allerdings das Problem, daß mir, wenn ich z.B. "23r" eingebe, trotzdem die 23 als float zurückgegeben wird, obwohl die Eingabe nicht korrekt ist.
Wenn ich jedoch "r" eingebe, reagiert das Programm so, wie es soll, nä(h)mlich mit der Aufforderung, doch bitte eine Zahl einzugeben.

Wie kann ich solche Fehleingaben vernünftig abfangen?
 
naja, wenn 23r korrekt als 23 eingelesen wird, ist das doch umso besser. Ist doch im Grunde nen toller service, dass cin des 'r' gleich entfernt... Versteh nicht ganz wo da das Problem ist...
 
Ganz einfach: "23r" ist einfach keine "richtige" Zahl...
 
Das ist das Default-Verhalten der >>-Operatoren für Zahlen. Wenn du danach keine weiteren Zeichen willst, mach folgendes:

1. mit >> float einlesen
2. stream auf .fail prüfen. falls fail kein float und schluss
3. falls kein fail auf .eof prüfen
4. falls .eof bist du fertig
5. falls nicht .eof nächstes zeichen mit .peek abfragen und auf whitespace prüfen
6. falls whitespace fertig, sonst kein float.
 
Danke für den Tipp.

Hat mich grade ein bischen Nerven gekostet. Nachdem ich dann auf den Trichter gekommen bin, zusätzlich zu whitespace auf '\n' zu prüfen, war das Nervenkostüm wieder in Ordnung.

cin.eof() gibt erst nach dem ersten erfolglosen Leseversuch true. Daher bringt diese Überprüfung nur was, wenn man nicht mit der Tastatur verknüpft ist, sondern mit einer Datenquelle, die ein unverrückbares Ende hat, z.B. einer Datei. Trotzdem lasse ich die Überprüfung drin.

Mein Code ist jetzt folgender:
Code:
float getInput(string name) {
  float tmpInput;
  while (true) {
    cout << name << ": ";
    cin >> tmpInput;
    if (!cin.fail()) {
      if (cin.eof() || cin.peek() == ' ' || cin.peek() == '\n') {
        getchar();
        cin.clear();
        cin.sync();
        return tmpInput;
      }
    }
    getchar();
    cin.clear();
    cin.sync();
    cout << "Naechster Versuch...\n";
  }
}
Und ich muss sagen:

Scheise, dem geht
 
Zuletzt bearbeitet:
So, ich hatte gerade Langeweile und hab mein IOStreams Buch gequält. Das hier sollte eine standardkonforme Variante sein, die eigentlich alle Eventualitäten abfängt. Das facet-Geraffel sieht eventuell etwas fies aus. :) Und aus dem Stream könnte man eigentlich noch einen Template-Parameter machen, damit das ganze auch mit wchar_t Streams funktioniert.

Achso, hatte ich vergessen zu erwähnen - whitespaces sind nicht nur Spaces, sondern auch \n und horizontale/vertikale Tabs.

Achso, man sollte niemals davon ausgehen, dass die Standardeingabe nur die Tastatur ist. Unter UNIX ist es absolut üblich, mal eben was an ein Programm zu pipen oder eine Datei an ein Programm rüberzuschaufeln.

Code:
#include <iostream>
#include <stdexcept>
#include <locale>

using namespace std;

template <typename T>
T readValue( istream& is = cin)
{
	T t = T();
	if( is.bad()) {
		throw runtime_error( "invalid stream");
	}
	if( is.eof()) {
		throw runtime_error( "nothing more to read");
	}
	is.clear();
	while( true) {
		cout << "bitte wert eingeben: " << flush;
		is >> t;

		if( is.bad()) {
			throw runtime_error( "invalid stream");
		}
		if( is.fail()) {
			// wert konnte nicht gelesen werden. stream zuruecksetzen
			is.clear();
			// naechstes zeichen ignorieren und von vorne
			is.ignore( 1);
		}
		else {
			if( !is.eof()) {
				// stream is nicht zuende

				// naechstes zeichen lesen
				istream::char_type c = istream::traits_type::to_char_type( is.peek());
				// und auf whitespace testen
				if( use_facet<ctype<istream::char_type> >(is.getloc()).is( ctype_base::space, c)) {
					// ist ein whitespace. zeichen ignorieren, dann wert zurueckgeben
					is.ignore( 1);
					return t;
				}
				else {
					// kein whitespace. zeichen ueberlesen und nochmal von vorne
					is.ignore( 1);
				}
			}
			else {
				// letzter wert im stream, also sind wir fertig
				return t;
			}
		}
		cout << "ungueltige eingabe. bitte nochmal." << endl;
	}
}

int main()
{
	double d = readValue<double>();
	cout << "gelesen: " << d << endl;
}
 
Zuletzt bearbeitet:
Danke, daß du dir nochmal diese Mühe gegeben hast.

Seit heute morgen ist das ganze dann für mich obsolet geworden. War ein Projekt in der Berufsschule. Und da in der Firma, in der ich meine Ausbildung mache, Java Schwerpunkt ist, habe ich bis zum nächsten Berufsschulblock keine Gelegenheit, Lust oder sonst irgendetwas, mich mit C/C++ auseinanderzusetzen.


Trotzdem danke.
 
Zurück
Oben