Wie kann C ++ eine Zeichenfolge (angegeben als char *) in ein int analysieren? Eine robuste und klare Fehlerbehandlung ist ein Plus (anstatt Null zurückzugeben ).
260
Wie kann C ++ eine Zeichenfolge (angegeben als char *) in ein int analysieren? Eine robuste und klare Fehlerbehandlung ist ein Plus (anstatt Null zurückzugeben ).
Antworten:
Im neuen C ++ 11 gibt es dafür Funktionen: stoi, stol, stoll, stoul und so weiter.
Bei einem Konvertierungsfehler wird eine Ausnahme ausgelöst.
Sogar diese neuen Funktionen haben immer noch das gleiche Problem wie Dan: Sie konvertieren die Zeichenfolge "11x" gerne in eine Ganzzahl "11".
Weitere Informationen : http://en.cppreference.com/w/cpp/string/basic_string/stol
quelle
Was nicht zu tun
Hier ist mein erster Ratschlag: Verwenden Sie hierfür keinen Stringstream . Während es auf den ersten Blick einfach zu sein scheint, werden Sie feststellen, dass Sie viel zusätzliche Arbeit leisten müssen, wenn Sie Robustheit und eine gute Fehlerbehandlung wünschen.
Hier ist ein Ansatz, der intuitiv zu funktionieren scheint:
Dies hat ein großes Problem:
str2int(i, "1337h4x0r")
Ich werde gerne zurückkehrentrue
undi
den Wert erhalten1337
. Wir können dieses Problem umgehen, indem wir sicherstellen, dassstringstream
nach der Konvertierung keine Zeichen mehr vorhanden sind :Wir haben ein Problem behoben, aber es gibt noch einige andere Probleme.
Was ist, wenn die Zahl in der Zeichenfolge nicht Basis 10 ist? Wir können versuchen, andere Basen aufzunehmen, indem wir den Stream auf den richtigen Modus einstellen (z. B.
ss << std::hex
), bevor wir die Konvertierung versuchen. Dies bedeutet jedoch, dass der Anrufer a priori wissen muss, auf welcher Basis die Nummer basiert - und wie kann der Anrufer dies möglicherweise wissen? Der Anrufer weiß noch nicht, wie die Nummer lautet. Sie wissen nicht einmal, dass es so isteine Zahl! Wie kann von ihnen erwartet werden, dass sie wissen, um welche Basis es sich handelt? Wir könnten einfach vorschreiben, dass alle in unsere Programme eingegebenen Zahlen Basis 10 sein müssen und hexadezimale oder oktale Eingaben als ungültig ablehnen müssen. Das ist aber nicht sehr flexibel oder robust. Es gibt keine einfache Lösung für dieses Problem. Sie können die Konvertierung nicht einfach einmal für jede Basis versuchen, da die Dezimalkonvertierung für Oktalzahlen (mit einer führenden Null) immer erfolgreich ist und die Oktalkonvertierung für einige Dezimalzahlen erfolgreich sein kann. Jetzt müssen Sie nach einer führenden Null suchen. Aber warte! Hexadezimalzahlen können auch mit einer führenden Null beginnen (0x ...). Seufzer.Selbst wenn es Ihnen gelingt, die oben genannten Probleme zu lösen, gibt es noch ein weiteres größeres Problem: Was ist, wenn der Anrufer zwischen einer schlechten Eingabe (z. B. "123foo") und einer Zahl unterscheiden muss, die außerhalb des Bereichs von
int
(z. B. "4000000000" für liegt? 32-Bitint
)? Mitstringstream
gibt es keine Möglichkeit, diese Unterscheidung zu treffen. Wir wissen nur, ob die Konvertierung erfolgreich war oder fehlgeschlagen ist. Wenn es fehlschlägt, können wir nicht wissen, warum es fehlgeschlagen ist. Wie Sie sehen,stringstream
lässt es zu wünschen übrig, wenn Sie Robustheit und klare Fehlerbehandlung wünschen.Dies führt mich zu meinem zweiten Ratschlag: machen keinen Gebrauch Erhöhung ist
lexical_cast
für diese . Überlegen Sie, was dielexical_cast
Dokumentation zu sagen hat:Was?? Wir haben bereits gesehen, dass
stringstream
das Kontrollniveau schlecht ist, und dennochstringstream
sollte es verwendet werden, anstatt,lexical_cast
wenn Sie "ein höheres Kontrollniveau" benötigen. Dalexical_cast
es sich nur um einen Wrapper handelt,stringstream
treten dieselben Problemestringstream
auf: schlechte Unterstützung für mehrere Zahlenbasen und schlechte Fehlerbehandlung.Die beste Lösung
Glücklicherweise hat jemand bereits alle oben genannten Probleme gelöst. Die C-Standardbibliothek enthält
strtol
und Familie, die keines dieser Probleme haben.Ziemlich einfach für etwas, das alle Fehlerfälle behandelt und auch eine beliebige Zahlenbasis von 2 bis 36 unterstützt. Wenn
base
Null (die Standardeinstellung) ist, wird versucht, von einer beliebigen Basis zu konvertieren. Oder der Aufrufer kann das dritte Argument angeben und angeben, dass die Konvertierung nur für eine bestimmte Basis versucht werden soll. Es ist robust und behandelt alle Fehler mit minimalem Aufwand.Andere Gründe zu bevorzugen
strtol
(und Familie):Es gibt absolut keinen guten Grund, eine andere Methode anzuwenden.
quelle
strtol
threadsicher sein. POSIX erfordert außerdem dieerrno
Verwendung eines threadlokalen Speichers. Selbst auf Nicht-POSIX-Systemen verwenden fast alle Implementierungenerrno
auf Multithread-Systemen threadlokalen Speicher. Der neueste C ++ - Standard musserrno
POSIX-kompatibel sein. Der neueste C-Standard erfordert ebenfallserrno
einen threadlokalen Speicher. Selbst unter Windows, das definitiv nicht POSIX-kompatibel ist,errno
ist es threadsicher und im weiteren Sinne auchstrtol
.std::stol
dafür, dass Ausnahmen angemessen ausgelöst werden, anstatt Konstanten zurückzugeben.std::stol
sogar zur C ++ - Sprache hinzugefügt wurde. Trotzdem halte ich es nicht für fair zu sagen, dass dies "C-Codierung in C ++" ist. Es ist albern zu sagen, dass diesstd::strtol
C-Codierung ist, wenn sie explizit Teil der C ++ - Sprache ist. Meine Antwort traf perfekt auf C ++ zu, als es geschrieben wurde, und es gilt auch mit dem neuenstd::stol
. Das Aufrufen von Funktionen, die Ausnahmen auslösen können, ist nicht immer für jede Programmiersituation die beste.Dies ist ein sicherer C-Weg als atoi ()
C ++ mit Standardbibliothek stringstream : (danke CMS )
Mit Boost- Bibliothek: (danke jk )
Bearbeiten: Die Stringstream-Version wurde so korrigiert, dass Fehler behandelt werden. (Dank des Kommentars von CMS und jk zum Originalbeitrag)
quelle
Der gute alte C-Weg funktioniert immer noch. Ich empfehle strtol oder strtoul. Zwischen dem Rückgabestatus und dem 'endPtr' können Sie eine gute Diagnoseausgabe geben. Es handhabt auch mehrere Basen gut.
quelle
Sie können Boosts verwenden
lexical_cast
, die dies in eine allgemeinere Oberfläche einschließen.lexical_cast<Target>(Source)
wirftbad_lexical_cast
auf Versagen.quelle
Sie können den a-Stringstream aus dem C ++ - Standardbibliothek verwenden:
Unter Stream-Fallstricke finden Sie Fallstricke bei der Fehlerbehandlung und bei Streams in C ++.
quelle
Sie können Stringstreams verwenden
quelle
Ich denke, diese drei Links fassen es zusammen:
stringstream- und lexical_cast-Lösungen entsprechen in etwa der Verwendung von stringstream durch lexical cast.
Einige Spezialisierungen der lexikalischen Besetzung verwenden einen anderen Ansatz. Weitere Informationen finden Sie unter http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp . Ganzzahlen und Gleitkommazahlen sind jetzt auf die Konvertierung von Ganzzahlen in Zeichenfolgen spezialisiert.
Man kann lexical_cast auf seine eigenen Bedürfnisse spezialisieren und es schnell machen. Dies wäre die ultimative Lösung, die alle Beteiligten zufriedenstellt, sauber und einfach.
Die bereits erwähnten Artikel zeigen einen Vergleich zwischen verschiedenen Methoden zum Konvertieren von Ganzzahlen <-> Zeichenfolgen. Folgende Ansätze sind sinnvoll: alter C-Way, Spirit.Karma, Fastformat, einfache naive Schleife.
Lexical_cast ist in einigen Fällen in Ordnung, z. B. für die Konvertierung von int in Zeichenfolgen.
Das Konvertieren von Zeichenfolgen in int mithilfe von lexikalischem Cast ist keine gute Idee, da es je nach verwendeter Plattform / verwendetem Compiler 10-40-mal langsamer als atoi ist.
Boost.Spirit.Karma scheint die schnellste Bibliothek zum Konvertieren von Ganzzahlen in Zeichenfolgen zu sein.
und einfache einfache Schleife aus dem oben erwähnten Artikel ist der schnellste Weg, um String in int zu konvertieren, offensichtlich nicht der sicherste, strtol () scheint eine sicherere Lösung zu sein
quelle
Die C ++ String Toolkit Library (StrTk) bietet die folgende Lösung:
Der InputIterator kann entweder aus vorzeichenlosen Iteratoren char *, char * oder std :: string bestehen, und es wird erwartet, dass T ein vorzeichenbehaftetes int ist, z. B. signiertes int, int oder long
quelle
v = (10 * v) + digit;
Überlauf unnötig mit String-Eingabe mit dem Textwert vonINT_MIN
. Tabelle ist von fragwürdigem Wert vs einfachdigit >= '0' && digit <= '9'
Wenn Sie C ++ 11, heute die entsprechenden Lösungen sind die C ++ integer Umwandlungsfunktionen in
<string>
:stoi
,stol
,stoul
,stoll
,stoull
. Sie werfen bei falscher Eingabe entsprechende Ausnahmen und nutzen die schnellen und kleinenstrto*
Funktionen unter der Haube.Wenn Sie mit einer früheren Version von C ++ nicht weiterkommen, können Sie diese Funktionen in Ihrer Implementierung nachahmen.
quelle
Ab C ++ 17 können Sie
std::from_chars
ab dem hier<charconv>
dokumentierten Header verwenden .Beispielsweise:
Als Bonus kann es auch andere Basen wie Hexadezimal verarbeiten.
quelle
Ich mag die Antwort von Dan Moulding , ich werde nur ein bisschen C ++ - Stil hinzufügen:
Es funktioniert sowohl für std :: string als auch für const char * durch die implizite Konvertierung. Es ist auch nützlich für die Basiskonvertierung, z. B. all
to_int("0x7b")
undto_int("0173")
undto_int("01111011", 2)
undto_int("0000007B", 16)
undto_int("11120", 3)
undto_int("3L", 34);
würde 123 zurückgeben.Im Gegensatz
std::stoi
dazu funktioniert es in Pre-C ++ 11. Auch im Gegensatz zustd::stoi
,boost::lexical_cast
undstringstream
es wirft Ausnahmen für seltsame Saiten wie "123hohoho".NB: Diese Funktion toleriert führende Leerzeichen, jedoch keine nachfolgenden Leerzeichen, dh
to_int(" 123")
gibt 123 zurück, während eineto_int("123 ")
Ausnahme ausgelöst wird. Stellen Sie sicher, dass dies für Ihren Anwendungsfall akzeptabel ist, oder passen Sie den Code an.Eine solche Funktion könnte Teil von STL sein ...
quelle
Ich kenne drei Möglichkeiten, String in int umzuwandeln:
Verwenden Sie entweder die Funktion stoi (String to int) oder wählen Sie einfach Stringstream, den dritten Weg zur individuellen Konvertierung. Der Code ist unten aufgeführt:
1. Methode
2. Methode
3. Methode - aber nicht für eine individuelle Konvertierung
quelle
Ich mag Dans Antwort , besonders wegen der Vermeidung von Ausnahmen. Für die Entwicklung eingebetteter Systeme und andere Systementwicklungen auf niedriger Ebene ist möglicherweise kein geeignetes Ausnahme-Framework verfügbar.
Nach einer gültigen Zeichenfolge wurde eine Prüfung auf Leerzeichen hinzugefügt ... diese drei Zeilen
Es wurde auch eine Überprüfung auf Analysefehler hinzugefügt.
Hier ist die komplette Funktion ..
quelle
" "
.strtol()
wird nicht festgelegt, um festzulegen,errno
wann keine Konvertierung erfolgt. Besser zu verwendenif (s == end) return INCONVERTIBLE;
, um keine Konvertierung zu erkennen. Und dannif (*s == '\0' || *end != '\0')
kann zuif (*end)
2) vereinfachen|| l > LONG_MAX
und|| l < LONG_MIN
keinen Zweck erfüllen - sie sind nie wahr.Sie können diese definierte Methode verwenden.
Und wenn Sie von String in eine Ganzzahl konvertieren würden, würden Sie einfach Folgendes tun.
Die Ausgabe wäre 102.
quelle
atoi
scheint angesichts anderer Antworten wie der akzeptierten nicht wie "C ++" zu seinstd::stoi()
.Ich weiß, dass dies eine ältere Frage ist, aber ich bin so oft darauf gestoßen und habe bis heute noch keine gut vorgestellte Lösung mit den folgenden Merkmalen gefunden:
Also, hier ist meine mit einem Testband. Da die C-Funktionen strtoull / strtoll unter der Haube verwendet werden, wird immer zuerst der größte verfügbare Typ konvertiert. Wenn Sie dann nicht den größten Typ verwenden, werden zusätzliche Bereichsprüfungen durchgeführt, um sicherzustellen, dass Ihr Typ nicht über- (unter) geflossen ist. Dafür ist es etwas weniger performant als wenn man strtol / strtoul richtig gewählt hat. Es funktioniert jedoch auch für Kurzfilme / Zeichen, und meines Wissens gibt es auch keine Standardbibliotheksfunktion, die dies tut.
Genießen; hoffentlich findet es jemand nützlich.
StringToDecimal
ist die User-Land-Methode; es ist überladen, so dass es entweder wie folgt aufgerufen werden kann:oder dieses:
Ich hasse es, den int-Typ zu wiederholen, also bevorzuge ich letzteren. Dies stellt sicher, dass bei einer Änderung des Typs "a" keine schlechten Ergebnisse erzielt werden. Ich wünschte, der Compiler könnte es so herausfinden:
... aber C ++ leitet keine Vorlagenrückgabetypen ab, das ist also das Beste, was ich bekommen kann.
Die Implementierung ist ziemlich einfach:
CstrtoxllWrapper
Wraps beidestrtoull
undstrtoll
, basierend auf der Signatur des Vorlagentyps, je nachdem, was erforderlich ist, und Bereitstellung einiger zusätzlicher Garantien (z. B. ist eine negative Eingabe nicht zulässig, wenn sie nicht signiert ist, und stellt sicher, dass die gesamte Zeichenfolge konvertiert wurde).CstrtoxllWrapper
wird vonStringToSigned
undStringToUnsigned
mit dem größten Typ (long long / unsigned long long) verwendet, der dem Compiler zur Verfügung steht; Dadurch kann die maximale Konvertierung durchgeführt werden. Falls erforderlich, führtStringToSigned
/ dannStringToUnsigned
die endgültigen Bereichsprüfungen für den zugrunde liegenden Typ durch. SchließlichStringToDecimal
entscheidet die Endpunktmethode basierend auf der Signatur des zugrunde liegenden Typs, welche der StringTo * -Vorlagenmethoden aufgerufen werden soll.Ich denke, der größte Teil des Mülls kann vom Compiler optimiert werden. Fast alles sollte zur Kompilierungszeit deterministisch sein. Jeder Kommentar zu diesem Aspekt wäre für mich interessant!
quelle
long long
stattintmax_t
?if (ePtr != str)
. Verwenden Sie außerdemisspace((unsigned char) *ePtr)
, um negative Werte von richtig zu behandeln*ePtr
.In C können Sie verwenden
int atoi (const char * str)
,Analysiert den C-String str und interpretiert seinen Inhalt als ganzzahlige Zahl, die als Wert vom Typ int zurückgegeben wird.
quelle
atoi
in der Frage verlinkt habe , bin ich mir dessen bewusst. Die Frage betrifft eindeutig nicht C, sondern C ++. -1