Das folgende Programm konvertiert eine Zeichenfolge in eine lange Zeichenfolge, gibt aber nach meinem Verständnis auch einen Fehler zurück. Ich verlasse mich darauf, dass wenn strtol
String erfolgreich in long umgewandelt, dann der zweite Parameter in strtol
sollte gleich NULL sein. Wenn ich die folgende Anwendung mit 55 ausführe, erhalte ich die folgende Meldung.
./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55
Wie kann ich Fehler von strtol erfolgreich erkennen? In meiner Anwendung ist Null ein gültiger Wert.
Code:
#include <stdio.h>
#include <stdlib.h>
static long parseLong(const char * str);
int main(int argc, char ** argv)
{
printf("%s as long is %ld\n", argv[1], parseLong(argv[1]));
return 0;
}
static long parseLong(const char * str)
{
long _val = 0;
char * temp;
_val = strtol(str, &temp, 0);
if(temp != '\0')
printf("Could not convert %s to long and leftover string is: %s", str, temp);
return _val;
}
Beachten Sie, dass Namen, die mit einem Unterstrich beginnen, für die Implementierung reserviert sind; Am besten vermeiden Sie die Verwendung solcher Namen in Ihrem Code. Somit, _val
sollte gerecht sein val
.
Die vollständige Spezifikation der Fehlerbehandlung für strtol()
und seine Verwandten ist komplex, überraschend komplex, wenn man ihm zum ersten Mal begegnet. Eine Sache, die Sie absolut richtig machen, ist die Verwendung einer Funktion zum Aufrufen strtol()
; Es ist wahrscheinlich nicht korrekt, es ‘roh’ im Code zu verwenden.
Da die Frage sowohl mit C als auch mit C++ gekennzeichnet ist, werde ich aus dem C2011-Standard zitieren; die entsprechende Formulierung finden Sie im C++-Standard selbst.
ISO/IEC 9899:2011 §7.22.1.4 Die strtol
, strtoll
, strtoul
und strtoull
Funktionen
long int strtol(const char * restrict nptr, char ** restrict endptr, int base);
¶2 […] Zuerst zerlegen sie die Eingabezeichenfolge in drei Teile: eine anfängliche, möglicherweise leere Folge von Leerzeichen (wie von der isspace-Funktion angegeben), eine Subjektsequenz, die einer Ganzzahl ähnelt, die in einer Basis dargestellt wird, die durch den Wert von base bestimmt wird, und eine abschließende Zeichenfolge aus einem oder mehreren nicht erkannten Zeichen, einschließlich des abschließenden Nullzeichens der Eingabezeichenfolge. […]
¶7 Wenn die Subjektsequenz leer ist oder nicht die erwartete Form hat, wird keine Konvertierung durchgeführt; der Wert von nptr
wird in dem Objekt gespeichert, auf das gezeigt wird endptr
unter der Vorraussetzung, dass endptr
ist kein Nullzeiger.
Kehrt zurück
¶8 Die strtol
, strtoll
, strtoul
und strtoull
Funktionen geben den konvertierten Wert zurück, falls vorhanden. Wenn keine Konvertierung durchgeführt werden konnte, wird Null zurückgegeben. Wenn der korrekte Wert außerhalb des Bereichs darstellbarer Werte liegt, wird LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX oder ULLONG_MAX zurückgegeben (je nach Rückgabetyp und Vorzeichen des Werts, falls vorhanden), und der Wert des Makros ERANGE ist darin gespeichert errno
.
Denken Sie daran, dass keine standardmäßige C-Bibliotheksfunktion jemals festgelegt wird errno
auf 0. Um zuverlässig zu sein, müssen Sie daher festlegen errno
vor dem Anruf auf Null strtol()
.
Also dein parseLong()
Funktion könnte so aussehen:
static long parseLong(const char *str)
{
errno = 0;
char *temp;
long val = strtol(str, &temp, 0);
if (temp == str || *temp != '\0' ||
((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n",
str, temp);
// cerr << "Could not convert '" << str << "' to long and leftover string is '"
// << temp << "'\n";
return val;
}
Beachten Sie, dass dies bei einem Fehler 0 oder LONG_MIN oder LONG_MAX zurückgibt, je nachdem, was passiert strtol()
ist zurückgekommen. Wenn Ihr aufrufender Code wissen muss, ob die Konvertierung erfolgreich war oder nicht, benötigen Sie eine andere Funktionsschnittstelle – siehe unten. Beachten Sie auch, dass Fehler gedruckt werden sollten stderr
eher, als stdout
und Fehlermeldungen sollten mit einem Zeilenumbruch abgeschlossen werden \n
; Wenn dies nicht der Fall ist, wird nicht garantiert, dass sie rechtzeitig erscheinen.
Nun, im Bibliothekscode möchten Sie wahrscheinlich kein Drucken, und Ihr aufrufender Code möchte möglicherweise wissen, ob die Konvertierung erfolgreich war oder nicht, sodass Sie möglicherweise auch die Schnittstelle überarbeiten. In diesem Fall würden Sie die Funktion wahrscheinlich so ändern, dass sie eine Erfolgs-/Fehleranzeige zurückgibt:
bool parseLong(const char *str, long *val)
{
char *temp;
bool rc = true;
errno = 0;
*val = strtol(str, &temp, 0);
if (temp == str || *temp != '\0' ||
((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
rc = false;
return rc;
}
die Sie verwenden könnten wie:
if (parseLong(str, &value))
…conversion successful…
else
…handle error…
Wenn Sie zwischen ‘nachgestelltem Junk’, ‘ungültiger numerischer Zeichenfolge’, ‘Wert zu groß’ und ‘Wert zu klein’ (und ‘kein Fehler’) unterscheiden müssen, verwenden Sie eine ganze Zahl oder enum
anstelle eines booleschen Rückgabecodes. Wenn Sie nachgestellte Leerzeichen, aber keine anderen Zeichen zulassen möchten, oder wenn Sie keine führenden Leerzeichen zulassen möchten, müssen Sie in der Funktion mehr tun. Der Code erlaubt Oktal, Dezimal und Hexadezimal; Wenn Sie eine strikte Dezimalzahl wünschen, müssen Sie die 0 im Aufruf auf 10 ändern strtol()
.
Wenn Ihre Funktionen als Teil der Standardbibliothek maskiert werden sollen, sollten sie nicht gesetzt werden errno
zu 0
dauerhaft, also müssten Sie den Code umschließen, um ihn beizubehalten errno
:
int saved = errno; // At the start, before errno = 0;
…rest of function…
if (errno == 0) // Before the return
errno = saved;
Du bist fast da. temp
selbst wird nicht null sein, aber es wird auf ein Nullzeichen zeigen, wenn die gesamte Zeichenfolge konvertiert wird, also müssen Sie es dereferenzieren:
if (*temp != '\0')
Wie kann ich Fehler von strtol erfolgreich erkennen?
static long parseLong(const char * str) {
int base = 0;
char *endptr;
errno = 0;
long val = strtol(str, &endptr, base);
3 Tests, die von der Standard-C-Bibliothek spezifiziert/unterstützt werden:
-
Irgendeine Konvertierung durchgeführt?
if (str == endptr) puts("No conversion.");
-
Im Bereich?
else if (errno == ERANGE) puts("Input out of long range.");
-
Tailing-Müll?
else if (*endptr) puts("Extra junk after the numeric text.");
Erfolg
else printf("Success %ld\n", val);
Geben Sie wie ein str == NULL
oder base
nicht 0, [2 to 36] ist undefiniertes Verhalten. Verschiedene Implementierungen (Erweiterungen der C-Bibliothek) bieten definiertes Verhalten und melden über errno
. Wir könnten einen 4. Test hinzufügen.
else if (errno) puts("Some implementation error found.");
Oder mit kombinieren errno == ERANGE
Prüfung.
Beispiel für knappen Code, der auch die Vorteile gängiger Implementierungserweiterungen nutzt.
long my_parseLong(const char *str, int base, bool *success) {
char *endptr = 0;
errno = 0;
long val = strtol(str, &endptr, base);
if (success) {
*success = endptr != str && errno == 0 && endptr && *endptr == '\0';
}
return val;
}
Ihnen fehlt eine Ebene der Indirektion. Sie wollen prüfen, ob die Charakter ist die Beendigung NUL
und nicht, wenn der Zeiger ist NULL
:
if (*temp != '\0')
Übrigens ist dies kein guter Ansatz für die Fehlerprüfung. Die richtige Fehlerprüfmethode der strto*
Familie von Funktionen wird nicht durch Vergleichen des Ausgabezeigers mit dem Ende der Zeichenfolge ausgeführt. Dies sollte erfolgen, indem auf einen Rückgabewert von Null geprüft und der Rückgabewert von abgerufen wird errno
.
Sie sollten überprüfen
*temp != '\0'
Sie sollten den Wert von errno auch nach dem Aufruf von strotol wie folgt überprüfen können:
RETURN VALUES
The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result
of the conversion, unless the value would underflow or overflow. If no conver-
sion could be performed, 0 is returned and the global variable errno is set to
EINVAL (the last feature is not portable across all platforms). If an overflow
or underflow occurs, errno is set to ERANGE and the function return value is
clamped according to the following table.
Function underflow overflow
strtol() LONG_MIN LONG_MAX
strtoll() LLONG_MIN LLONG_MAX
strtoimax() INTMAX_MIN INTMAX_MAX
strtoq() LLONG_MIN LLONG_MAX
Lesen Sie die Dokumentation erneut; Sie sollten auch Fehler wie Überlauf behandeln.
– Kerrek SB
5. Januar 2013 um 20:36 Uhr
Auch die richtige Fehlerprüfung für
strto*
Funktionen werden nicht durch Überprüfen des Ausgabezeigers ausgeführt. Dies sollte durch Prüfen auf einen Null-Rückgabewert und einen Satz erfolgenerrno
.– Benutzer529758
5. Januar 2013 um 20:37 Uhr
Warum benutzt du nicht
std::stoi
in C++? (Sie haben das C++-Tag hinzugefügt)– BatchyX
5. Januar 2013 um 20:39 Uhr
@BatchyX, Es funktioniert nicht so gut für Zeichenfolgen wie “123abc” (wie der Konsens in meiner vorherigen Frage war). Das OP prüft, ob die gesamte Zeichenfolge konvertiert werden soll.
– Chris
5. Januar 2013 um 20:48 Uhr
@chris: Du kannst genau dasselbe mit machen
std::stoi
. Tatsächlich ist der Prototyp vonstoi
ist fast das gleiche wiestrtol
verwendet aber Ausnahmen, wo Ausnahmen fällig sind, anstelle eines Fehlerrückgabewerts mit globaler Fehlervariable hackery.– BatchyX
5. Januar 2013 um 20:53 Uhr