2010-01-28 11 views
7

Ich habe ein Problem mit einem Programm, an dem ich in C++ arbeite. Ich bitte den Benutzer, eine gültige Nummer einzugeben. Ich nehme es als eine Zeichenkette auf, weil die bestimmte Aufgabe, die ich mache, es auf lange Sicht einfacher macht. Für die grundlegende Fehlerprüfung möchte ich überprüfen, ob die eingegebene Nummer eine gültige Nummer ist. Beispiel:Wie kann ich überprüfen, ob eine als String gespeicherte Zahl (double type) eine gültige doppelte Zahl in C++ ist?

Enter number: 3.14 
This would be valid 

Enter number: 3.1456.365.12 
This shouldn't be valid 

Antwort

7

Ich denke, boost :: lexical_cast Sie hier

+0

Ja, es wird eine ‚bad_lexical_cast‘ Ausnahme auslösen, wenn es nicht erfolgreich umgesetzt werden kann. Also stell sicher, dass du es immer in einem try/catch anrufst. –

+2

boost :: lexical_cast basiert auf std :: istringstream, so dass fehlerhafte Eingaben vom Typ "3.14.15" nicht erkannt werden. – Manuel

+3

@Manuel: Eigentlich tut es das. Außerdem wird überprüft, ob die * gesamte * Zeichenfolge erfolgreich konvertiert wurde. (Ja, mit stringstream können Sie das auch überprüfen.) Nicht zu sicher, ob dieses Verhalten costumizable ist (man braucht diese Überprüfung nicht immer) ... – UncleBens

3

helfen sollten Wenn Sie keine Schub haben, können Sie immer strtod

0

Ah verwenden können, ich liebte diese Zuordnungen. Ein guter alter Hand geschriebener Lexer ist der Weg zu gehen (da Sie noch in den Anfangstagen sind - versuchen Sie nicht, boost gerade noch zu verwenden). Sie sind schnell, einfach zu schreiben und extrem lustig zu spielen. Wenn Sie eine Kopie von Levines Buch über Lex/Yacc erhalten können, schauen Sie sich die ersten paar Kapitel für Ideen an.

+1

Sie sind auch leicht falsch, vor allem wenn Sie nicht falsch liegen ziemlich klar über die zulässigen Formate. Es macht Spaß, ein paar Lexer zu schreiben, aber lassen Sie sie aus dem Produktionscode heraus. –

+0

Ich dachte, das wäre eine Aufgabe wie bei den Hausaufgaben. Hm. – dirkgently

3

können Sie verwenden strtoX (wobei X f für Schwimmer, l für lange, ul für unsigned long, etc.), für die Art der Nummer wählen Sie wollen. Einer der Parameter, die Sie angeben, ist ein "end pointer", der auf das erste Zeichen in der Zeichenfolge verweist, das in den Zielnummerntyp nicht konvertiert werden konnte.

In Ihrem Fall suchen Sie offenbar, dass der Endzeiger am Ende der Zeichenfolge stehen muss, um anzuzeigen, dass alle Zeichen in der Zeichenfolge in den Zieltyp konvertiert wurden.

Edit: Sorry, habe nicht bemerkt, dass du "double" im Titel erwähnt hast (aber nicht die Frage selbst). In diesem Fall würden Sie strtod verwenden, wie ein paar andere auch empfohlen haben.

2

Der beste Weg ist, einen tatsächlichen Versuch zu machen, um Ihre Zeichenfolge double unter Verwendung eines beliebigen der Standard- und/oder idiomatische Weise wandeln die Umwandlung zu tun, und danach auf Fehler überprüfen. In C wären das Funktionen aus der strto... Gruppe (die natürlich auch in C++ perfekt verwendbar sind). In C++ können Sie Stream-basierte Konvertierungssprache verwenden.

Eine Sache, auf die man achten sollte, ist, dass die übliche Konvention in Standardkonvertierungsmethoden darin besteht, "so viel wie möglich" zu konvertieren und keine zusätzlichen Zeichen als Fehler zu betrachten. Zum Beispiel wird eine Zeichenkette "123abc" normalerweise als gültige Eingabe betrachtet, wobei nur der Teil "123" umgewandelt wird. Alle verwendbaren Methoden bieten Ihnen die Möglichkeit, die Tatsache zu erkennen, dass nach der tatsächlichen Zahl noch etwas vorhanden ist, wenn Sie diese Situation als Fehler behandeln wollen. Es liegt jedoch an Ihnen, die zusätzlichen Schritte zur Durchführung dieser Überprüfung durchzuführen.

9

Verwenden Sie strtod, das eine Zeichenfolge in ein Double konvertiert und alle Zeichen zurückgibt, die nicht als Teil des Double interpretiert werden konnten.

double strtod(const char* nptr, char** endptr) 

So:

char* input = "3.1456.365.12"; 
char* end; 

strtod(input, &end); 
if (*input == '\0') 
{ 
    printf("fail due to empty string\n"); 
} 
if (end == input || *end != '\0') 
{ 
    printf("fail - the following characters are not part of a double\n%s\n", end); 
} 
+0

jetzt getestet, funktioniert. –

+0

Es kann auch aufgrund von Unter- und Überlauf fehlschlagen, die mit diesem Code nicht erkannt werden. Ich denke, du musst auch auf "inf" testen und errno überprüfen, um sicher zu gehen. – UncleBens

1

Eine einfache Möglichkeit ist, die sscanf-Funktion:

const char * num_as_str = "3.1416"; 
double num; 

if(std::sscanf(num_as_str, "%lg", &num) == 1) 
{ 
    std::cout << "You correctly entered the number " << num << "\n"; 
} 

Wenn Sie Lust bekommen möchten, können Sie istringstream verwenden:

std::istringstream iss(num_as_str); 
if(iss >> num) 
{ 
    std::cout << "You correctly entered the number " << num << "\n"; 
} 

Wenn Sie e xtra-fancy können Sie boost des lexical_cast verwenden:

try 
{ 
    num = boost::lexical_cast<double>(num_as_str); 
} 
catch(boost::bad_lexical_cast &) 
{ 
    std::cout << "What you entered is not a proper number" << num << "\n"; 
} 
+1

Ihr Stringstream-Beispiel ist falsch. Sein Beispiel von "3.1456.365.12" wird Erfolg melden. – luke

+1

Ups, das stimmt. Und die Lösungen von sscanf und lexical_cast haben das gleiche Problem. Sieht aus, als wäre die richtige Lösung die von Luke. – Manuel

4

Ein Beispiel unter Verwendung von nur Standard C++:

#include <sstream> 

// ... 

double dbl = 0.0; 
std::istringstream num("3.1456.365.12"); 

num >> dbl; 

if(!num.fail() && 
    num.eof()) // This second test is important! This makes sure that the entire string was converted to a number 
{ 
    // success 
} 
else 
{ 
    // failure 
} 

Bonus generische Template-Funktion Version:

#include <sstream> 
#include <string> 
#include <exception> 

// Version that throws an exception on a bad parse: 

template <typename T> 
T parse(const std::string& str) 
{ 
    T value; 
    std::istringstream parser(str); 
    parser >> value; 

    if(!parser.fail() && parser.eof()) 
    { 
     return value; 
    } 
    else 
    { 
     throw "bad lexical cast"; 
    } 
} 

// usage: 

double foo = parse<double>("3.14234"); 

// Non-exception, error code version 

template <typename T> 
bool parse(const std::string& str, T& value) 
{ 
    std::istringstream parser(str); 
    parser >> value; 

    return (!parser.fail() && parser.eof()); 
} 

// usage: 

double foo = 0.0; 
bool success = parser<double>("3.11234", foo); 
+0

sollte es nicht 'num >> dbl;' anstelle von 'num >> double;' in Ihrem ersten Beispiel? .. – smerlin

+0

Ja, netter Fang :) – luke

0

Wie AndreyT erwähnt, ist die beste So wird versucht, die Zeichenfolge in einen Gleitkommawert zu konvertieren und nach einem Fehler zu suchen. Persönlich würde ich std :: istringstream verwenden, da Sie C++ verwenden. So etwas wie die folgenden funktionieren sollte:

float ff; 
std::istringstream istr; 
std::string input("1234.5678"); 

// set the stream to have your string as its base 
istr.str(input); 

// now try to read the number: 
istr >> ff; 
if (istr.fail()) 
{ 
    // some error in the parsing 
} 

istringstream Teil der STL ist, so dass Sie keine zusätzlichen Bibliotheken benötigen, und es wird auch mit Ausnahmen, wenn das ist Ihre Wahl. Weitere Informationen finden Sie hier: http://www.cplusplus.com/reference/iostream/istringstream/

+0

Genau wie Manuel's Antwort wird Ihr Beispiel Erfolg auf Johns Beispiel falsch berichten "3.1456.365.12 " – luke

+0

Ja, sieht so aus, das ist ein gutes Beispiel für C++ gotcha :) – Manuel

+0

Yup :) Es lohnt sich zu testen. – luke

0

Sie könnten reguläre Ausdrücke verwenden. Da Sie bereits eine Zeichenfolge haben, wäre es leicht sein, dass mit diesem regulären Ausdruck vergleichen:

/^\d+(\.\d+)?$/ 

Die Bibliothek regex.h Ihnen hier helfen kann. Siehe dazu: regex.h

+0

Was ist mit wissenschaftlicher Notation? Überprüfen, dass es tatsächlich in den doppelten Bereich passt? (Wenn Sie ein Problem haben, wird Regex vorgeschlagen. Jetzt haben Sie zwei Probleme . :)) – UncleBens

+0

Wer hat etwas über die Überprüfung der doppelten Reichweite oder wissenschaftlichen Notation gesagt? ;-) RegExes sind großartig, aber ich gebe zu, dass es schwierig ist, zu verifizieren, dass sie korrekt sind. – Aaron

0

Dies ist mein schneller Hack :)

#include <iostream> 
#include <string> 
#include <sstream> 
using namespace std; 

template <typename T> 
bool fromStr(const std::string& str, T& var) 
{ 
    std::stringstream convertor; 
    convertor << str; 
    convertor >> var; 

    if(convertor.fail()) 
     return false; 

    char c = static_cast<char>(convertor.get()); 

    return convertor.eof() || c == '\n' || c == ' ' || c == '\t'; 
} 

int main() 
{ 
    double d; 
    std::string str = "5.04146.55"; 

    if(fromStr<double>(str, d)) 
    { 
     std::cout << "Valid conversion!, d = " << d; 
    } 
    else 
    { 
     std::cout << "Invalid conversion!"; 
    } 
} 
Verwandte Themen