Was ist der beste Weg, um eine Eingabe-Validierung in C ++ mit cin durchzuführen?

7

Mein Bruder hat vor kurzem begonnen, C ++ zu lernen. Er erzählte mir von einem Problem, das er beim Versuch, Eingaben in einem einfachen Programm zu validieren, antraf. Er hatte ein Textmenü, in dem der Benutzer eine Ganzzahl choice eingab, wenn sie eine ungültige Auswahl eingaben, würden sie gebeten werden, sie wieder einzugeben (while-Schleife). Wenn der Benutzer jedoch eine Zeichenfolge anstelle eines int eingibt, wird der Code unterbrochen. Ich las verschiedene Fragen zu stackoverflow und bat ihn, seinen Code wie folgt umzuschreiben:

%Vor%

Während das in Ordnung war, habe ich auch ein paar andere Ideen ausprobiert:
1. Verwenden Sie einen try catch-Block. Es hat nicht funktioniert. Ich denke, das liegt daran, dass eine Ausnahme aufgrund schlechter Eingaben nicht ausgelöst wird. 2. Ich habe if(! cin){//Do Something} ausprobiert, was auch nicht funktioniert hat. Ich habe das noch nicht herausgefunden 3. Drittens habe ich versucht, eine Zeichenfolge mit fester Länge einzugeben und sie dann zu analysieren. Ich würde atoi () verwenden. Ist das normkonform und portabel? Soll ich meine eigene Parsing-Funktion schreiben? 4. Wenn Sie eine Klasse schreiben, die cin verwendet, aber diese Art der Fehlererkennung dynamisch durchführt, indem sie z. B. den Typ der Eingabevariablen zur Laufzeit feststellt, hätte sie zu viel Aufwand? Ist es überhaupt möglich?

Ich würde gerne wissen, was der beste Weg ist, um diese Art von Überprüfung zu machen, was sind die besten Praktiken?

Ich möchte hinzufügen, dass ich neu beim Schreiben von C ++ - Code bin, aber ich bin neu beim Schreiben von Standards, die mit dem Standard konform sind. Ich versuche, schlechte Praktiken zu verlernen und die richtigen zu lernen. Ich wäre sehr dankbar, wenn die Beantworter eine detaillierte Erklärung abgeben würden.

BEARBEITEN : Ich sehe, dass litb eine meiner vorherigen Bearbeitungen beantwortet hat. Ich werde diesen Code hier als Referenz veröffentlichen.

%Vor%

Dieser Code schlägt bei Eingaben wie "1asdsdf" fehl. Ich wusste nicht, wie ich es beheben sollte, aber Litb hat eine großartige Antwort geschrieben. :)

    
batbrat 15.10.2009, 13:36
quelle

8 Antworten

14

Hier ist Code, mit dem Sie sicherstellen können, dass Sie auch Dinge wie

ablehnen %Vor%

Wo nicht nummerierte Zeichen der Nummer folgen. Wenn Sie die ganze Zeile lesen und dann analysieren und Aktionen entsprechend ausführen, müssen Sie möglicherweise die Art ändern, wie Ihr Programm arbeitet. Wenn Ihr Programm Ihre Nummer von verschiedenen Orten bis jetzt gelesen hat, müssen Sie einen zentralen Ort eingeben, der eine Eingabezeile analysiert und über die Aktion entscheidet. Aber vielleicht ist das auch eine gute Sache - so könnten Sie die Lesbarkeit des Codes verbessern, indem Sie Dinge trennen: I nput - P rocessing - O Ausgabe

Wie auch immer, hier ist, wie Sie die Anzahl-nicht-Nummer von oben ablehnen können. Lies eine Zeile in eine Zeichenkette und analysiere sie dann mit stringstream :

%Vor%

Es isst alle nachfolgenden Leerzeichen. Wenn es beim Lesen der Ganzzahl oder des nachgestellten Leerzeichens das Dateiende des Stringstreams trifft, setzt es das eof-Bit und überprüft dies. Wenn es an der ersten Stelle keine Ganzzahl gelesen hat, dann wurde das Fail- oder das Bad-Bit gesetzt.

Frühere Versionen dieser Antwort verwendeten std::cin direkt - aber std::ws funktioniert nicht gut zusammen mit std::cin , das an ein Terminal angeschlossen ist (es blockiert stattdessen, dass der Benutzer etwas eingibt), also verwenden wir a stringstream zum Lesen der ganzen Zahl.

Beantworten Sie einige Ihrer Fragen:

Frage: 1. Einen try-catch-Block verwenden. Es hat nicht funktioniert. Ich denke, das liegt daran, dass eine Ausnahme wegen schlechter Eingabe nicht ausgelöst wird.

Antwort: Nun können Sie dem Stream sagen, dass er Ausnahmen auslöst, wenn Sie etwas lesen. Sie verwenden die Funktion istream::exceptions , die Sie angeben, für welche Art von Fehler eine Ausnahme ausgelöst werden soll:

%Vor%

Ich habe es nie benutzt. Wenn Sie dies in std::cin tun, müssen Sie daran denken, die Flags für andere Leser wiederherzustellen, die darauf angewiesen sind, dass sie nicht werfen. Es ist viel einfacher, einfach die Funktionen fail und bad zu verwenden, um nach dem Status des Streams zu fragen.

Frage: 2. Ich habe if(!cin){ //Do Something } versucht, was auch nicht funktioniert hat. Ich habe das noch nicht herausgefunden.

Antwort: Das könnte von der Tatsache herrühren, dass du ihm so etwas wie "42crap" gegeben hast. Für den Stream ist das eine vollständig gültige Eingabe, wenn eine Extraktion in eine ganze Zahl durchgeführt wird.

Frage: 3. Drittens habe ich versucht, eine Zeichenfolge fester Länge einzugeben und sie dann zu analysieren. Ich würde atoi () verwenden. Ist das normkonform und portabel? Soll ich meine eigene Parsing-Funktion schreiben?

Antwort: atoi ist Standardkonform. Aber es ist nicht gut, wenn Sie nach Fehlern suchen möchten. Es wird keine Fehlerprüfung durchgeführt, im Gegensatz zu anderen Funktionen. Wenn Sie eine Zeichenkette haben und prüfen möchten, ob sie eine Zahl enthält, dann tun Sie es wie im obigen Code oben.

Es gibt C-ähnliche Funktionen, die direkt von einer C-Zeichenfolge lesen können. Sie existieren, um die Interaktion mit altem, altem Code zu ermöglichen und schnell performanten Code zu schreiben. Man sollte sie in Programmen vermeiden, weil sie eher auf niedriger Ebene arbeiten und rohe nackte Zeiger benötigen. Aufgrund ihrer Natur können sie auch nicht so erweitert werden, dass sie mit benutzerdefinierten Typen arbeiten. Konkret geht es dabei um die Funktion "strtol" (string-to-long), die grundsätzlich mit Fehlerüberprüfung und der Fähigkeit zur Zusammenarbeit mit anderen Basen (zB hex) ausgestattet ist.

Frage: 4. Wenn ich eine Klasse schreibe, die cin verwendet, aber diese Art der Fehlererkennung dynamisch durchführt, indem sie z. B. den Typ der Eingabevariablen zur Laufzeit feststellt, hat sie zu viel Aufwand ? Ist es überhaupt möglich?

Antwort: Im Allgemeinen müssen Sie sich hier nicht allzu viel um den Overhead kümmern (wenn Sie Laufzeit-Overhead meinen). Aber es hängt genau davon ab, wo Sie diese Klasse verwenden. Diese Frage wird sehr wichtig sein, wenn Sie ein leistungsstarkes System schreiben, das Eingaben verarbeitet und durchgängig hoch sein muss. Aber wenn Sie Eingaben von einem Terminal oder einer Datei lesen müssen, sehen Sie bereits, worauf es ankommt: Warten auf die Eingabe des Benutzers dauert wirklich so lange, dass Sie die Laufzeitkosten zu diesem Zeitpunkt nicht mehr überwachen müssen Rahmen.

Wenn Sie Code-Overhead meinen - dann hängt es davon ab, wie der Code implementiert ist. Sie müssten Ihre gelesene Zeichenfolge überprüfen - ob sie eine Zahl enthält oder nicht, ob es sich um eine beliebige Zeichenfolge handelt. Abhängig davon, was Sie scannen möchten (vielleicht haben Sie auch eine "Datumseingabe" oder ein "Zeit" -Eingabeformat. Schauen Sie in boost.date_time dafür), Ihr Code kann beliebig komplex werden. Für einfache Dinge wie die Klassifizierung zwischen Zahlen oder nicht, denke ich, dass Sie mit einer kleinen Menge Code davonkommen können.

    
Johannes Schaub - litb 13.02.2009, 15:57
quelle
12

Dies ist, was ich mit C mache, aber es ist wahrscheinlich auch für C ++ anwendbar.

Geben Sie alles als Zeichenfolge ein.

Dann und nur dann parsen Sie die Zeichenfolge in das, was Sie brauchen. Manchmal ist es besser, wenn Sie Ihre eigenen codieren, als wenn Sie versuchen, jemand anderen nach Ihrem Willen zu beugen.

    
paxdiablo 13.02.2009 13:29
quelle
4
  • Um die Ausnahmen von iostreams zu erhalten, müssen Sie das richtige Flag für die Ausnahme festlegen der Strom.
  • Und ich würde get_line verwenden, um die gesamte Eingabezeile zu erhalten und sie dann entsprechend zu behandeln - benutze lexical_cast, reguläre Ausdrücke (zum Beispiel Boost Regex oder Boost Xpressive , analysiere es mit Boost Spirit , oder verwende einfach eine geeignete Logik
Anonymous 13.02.2009 13:31
quelle
3

Was ich tun würde, ist zweifach: Versuchen Sie zunächst, die Eingabe zu validieren und die Daten mit einem regulären Ausdruck zu extrahieren, wenn die Eingabe etwas nicht trivial ist. Es kann auch sehr hilfreich sein, wenn die Eingabe nur eine Reihe von Zahlen ist.

Dann benutze ich gerne boost :: lexical_cast kann eine bad_ lexical_cast Ausnahme auslösen, wenn die Eingabe nicht konvertiert werden kann.

In Ihrem Beispiel:

%Vor%     
Diego Sevilla 13.02.2009 13:28
quelle
2

Vergessen Sie die Verwendung der formatierten Eingabe (der Operator & lt; & gt;) direkt in echtem Code. Sie müssen immer rohen Text mit std :: getline oder ähnlichem lesen und dann Ihre eigenen Eingabeanalyse-Routinen (die den & gt; & gt; -Operator verwenden können) verwenden, um die Eingabe zu analysieren.

    
anon 13.02.2009 13:30
quelle
2

Wie wäre es mit einer Kombination der verschiedenen Ansätze:

  1. Snag die Eingabe von std::cin mit std::getline(std::cin, strObj) wobei strObj ein std::string Objekt ist.

  2. Verwenden Sie boost::lexical_cast , um eine lexikalische Übersetzung von strObj in eine vorzeichenbehaftete oder vorzeichenlose Ganzzahl mit der größten Breite (z. B. unsigned long long oder etwas Ähnliches)

  3. auszuführen
  4. Verwenden Sie boost::numeric_cast , um die ganze Zahl auf den erwarteten Bereich zu reduzieren.

Sie könnten einfach die Eingabe mit std::getline holen und dann boost::lexical_cast auf den entsprechend schmalen Integer-Typ setzen, abhängig davon, wo Sie den Fehler abfangen wollen. Der dreistufige Ansatz hat den Vorteil, dass alle Integer-Daten akzeptiert werden und die Fehler einzeln getrennt werden.

    
D.Shawley 13.02.2009 14:58
quelle
1

Ich stimme mit Pax überein, der einfachste Weg dies zu tun ist, alles als String zu lesen und TryParse zu benutzen, um die Eingabe zu verifizieren. Wenn es im richtigen Format ist, dann fahren Sie fort, anderenfalls benachrichtigen Sie den Benutzer einfach und verwenden Sie weiter auf der Schleife.

    
David Božjak 13.02.2009 15:28
quelle
1

Eine Sache, die noch nicht erwähnt wurde, ist, dass es normalerweise wichtig ist, dass Sie testen, ob das cin & gt; & gt; Operation arbeitete vor der Verwendung der Variable, die angeblich etwas aus dem Stream haben.

Dieses Beispiel ist ähnlich wie Ihres, macht aber diesen Test.

%Vor%

Dies wird den üblichen Operator & gt; & gt; Semantik; Es wird zuerst Whitespace übersprungen, dann versucht es so viele Ziffern wie möglich zu lesen und dann zu stoppen. Also "42crap" wird dir die 42 geben und dann den "Mist" überspringen. Wenn das nicht das ist, was Sie wollen, dann stimme ich den vorherigen Antworten zu, Sie sollten es in eine Zeichenfolge lesen und dann validieren (vielleicht mit einem regulären Ausdruck - aber das kann für eine einfache numerische Sequenz übertrieben sein).

    
Brian Neal 13.02.2009 17:03
quelle

Tags und Links