Wie konvertiert man eine Zeichenfolge in C in eine Ganzzahl?

Lesezeit: 9 Minuten

Benutzeravatar von user618677
Benutzer618677

Ich versuche herauszufinden, ob es eine alternative Möglichkeit gibt, Strings in C in Integer umzuwandeln.

Ich mustere regelmäßig Folgendes in meinem Code.

char s[] = "45";

int num = atoi(s);

Gibt es also einen besseren oder einen anderen Weg?

  • @ Yann, Entschuldigung für diese Verwirrung. Ich bevorzuge C.

    – Benutzer618677

    11. August 2011 um 16:16 Uhr

  • programmierungsimplified.com/c/source-code/…

    – Kyle Bridenstine

    28. September 2014 um 1:07 Uhr

  • Es funktioniert, aber es ist nicht der empfohlene Weg, da es keine Möglichkeit gibt, Fehler zu behandeln. Verwenden Sie dies niemals im Produktionscode, es sei denn, Sie können der Eingabe zu 100 % vertrauen.

    – Uwe Geuder

    5. November 2015 um 17:47 Uhr

  • @EJP Nur um mich zu verbessern.

    – Benutzer618677

    3. August 2018 um 1:32 Uhr

Benutzeravatar von cnicutar
Cnicutar

Es gibt strtol das ist besser IMO. Auch ich habe Gefallen gefunden strtonumverwenden Sie es also, wenn Sie es haben (aber denken Sie daran, dass es nicht portabel ist):

long long
     strtonum(const char *nptr, long long minval, long long maxval,
     const char **errstr);

Das könnte Sie auch interessieren strtoumax und strtoimax die Standardfunktionen in C99 sind. Du könntest zum Beispiel sagen:

uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
    /* Could not convert. */

Bleiben Sie jedenfalls fern atoi:

Der Aufruf atoi(str) ist äquivalent zu:

(int) strtol(str, (char **)NULL, 10)

außer dass die Behandlung von Fehlern abweichen kann. Wenn der Wert nicht dargestellt werden kann, ist das Verhalten undefiniert.

  • wofür muss ich eintragen strtonum? Ich bekomme immer eine implizite Deklarationswarnung

    – jsj

    30. März 2013 um 1:57 Uhr


  • @ trideceth12 Auf Systemen, auf denen es verfügbar ist, sollte es deklariert werden #<stdlib.h>. Sie können jedoch den Standard verwenden strtoumax Alternative.

    – Cnicutar

    30. März 2013 um 10:21 Uhr

  • Diese Antwort scheint nicht kürzer zu sein als der erste Code des Fragestellers.

    – Azurespot

    3. September 2014 um 4:46 Uhr

  • @NoniA. Prägnanz ist immer gut, aber nicht auf Kosten der Korrektheit.

    – Cnicutar

    3. September 2014 um 9:33 Uhr

  • Nicht so sehr falsch als unsicher. atoi() funktioniert, wenn die Eingabe gültig ist. Aber was ist, wenn Sie atoi (“Katze”) tun? strtol() hat ein definiertes Verhalten, wenn der Wert nicht als long dargestellt werden kann, atoi() nicht.

    – Daniel B.

    22. Juli 2015 um 19:13 Uhr

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

Robuster C89 strtol-basierte Lösung

Mit:

  • kein undefiniertes Verhalten (wie es bei der atoi Familie)
  • eine strengere Definition von Integer als strtol (z. B. kein führendes Leerzeichen oder nachgestellte Papierkorbzeichen)
  • Klassifizierung des Fehlerfalls (z. B. um Benutzern nützliche Fehlermeldungen zu geben)
  • eine “Testsuite”
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

typedef enum {
    STR2INT_SUCCESS,
    STR2INT_OVERFLOW,
    STR2INT_UNDERFLOW,
    STR2INT_INCONVERTIBLE
} str2int_errno;

/* Convert string s to int out.
 *
 * @param[out] out The converted int. Cannot be NULL.
 *
 * @param[in] s Input string to be converted.
 *
 *     The format is the same as strtol,
 *     except that the following are inconvertible:
 *
 *     - empty string
 *     - leading whitespace
 *     - any trailing characters that are not part of the number
 *
 *     Cannot be NULL.
 *
 * @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
 *
 * @return Indicates if the operation succeeded, or why it failed.
 */
str2int_errno str2int(int *out, char *s, int base) {
    char *end;
    if (s[0] == '\0' || isspace(s[0]))
        return STR2INT_INCONVERTIBLE;
    errno = 0;
    long l = strtol(s, &end, base);
    /* Both checks are needed because INT_MAX == LONG_MAX is possible. */
    if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
        return STR2INT_OVERFLOW;
    if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
        return STR2INT_UNDERFLOW;
    if (*end != '\0')
        return STR2INT_INCONVERTIBLE;
    *out = l;
    return STR2INT_SUCCESS;
}

int main(void) {
    int i;
    /* Lazy to calculate this size properly. */
    char s[256];

    /* Simple case. */
    assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
    assert(i == 11);

    /* Negative number . */
    assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
    assert(i == -11);

    /* Different base. */
    assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
    assert(i == 17);

    /* 0 */
    assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
    assert(i == 0);

    /* INT_MAX. */
    sprintf(s, "%d", INT_MAX);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MAX);

    /* INT_MIN. */
    sprintf(s, "%d", INT_MIN);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MIN);

    /* Leading and trailing space. */
    assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);

    /* Trash characters. */
    assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);

    /* int overflow.
     *
     * `if` needed to avoid undefined behaviour
     * on `INT_MAX + 1` if INT_MAX == LONG_MAX.
     */
    if (INT_MAX < LONG_MAX) {
        sprintf(s, "%ld", (long int)INT_MAX + 1L);
        assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
    }

    /* int underflow */
    if (LONG_MIN < INT_MIN) {
        sprintf(s, "%ld", (long int)INT_MIN - 1L);
        assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
    }

    /* long overflow */
    sprintf(s, "%ld0", LONG_MAX);
    assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);

    /* long underflow */
    sprintf(s, "%ld0", LONG_MIN);
    assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);

    return EXIT_SUCCESS;
}

GitHub-Upstream.

Basierend auf: https://stackoverflow.com/a/6154614/895245

  • Schön robust str2int(). Pedant: verwenden isspace((unsigned char) s[0]).

    – chux – Wiedereinsetzung von Monica

    30. Mai 2016 um 12:09 Uhr

  • @chux danke! Können Sie etwas näher erklären, warum die (unsigned char) Besetzung könnte einen Unterschied machen?

    – Ciro Santilli OurBigBook.com

    30. Mai 2016 um 13:00 Uhr

  • Der IAR C-Compiler warnt davor l > INT_MAX und l < INT_MIN sind sinnlose Integer-Vergleiche, da beide Ergebnisse immer falsch sind. Was passiert, wenn ich sie zu ändere? l >= INT_MAX und l <= INT_MIN um die Warnungen zu löschen? Auf ARM C, lang und int sind 32-Bit-signiert Grundlegende Datentypen in ARM C und C++

    – Ecle

    26. Januar 2017 um 2:23 Uhr


  • @ecle Code ändern in l >= INT_MAX führt zu fehlerhafter Funktionalität: Beispielrückgabe STR2INT_OVERFLOW mit Eingang "32767" und 16bit int. Verwenden Sie eine bedingte Kompilierung. Beispiel.

    – chux – Wiedereinsetzung von Monica

    20. Dezember 2017 um 12:45 Uhr

  • if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW; wäre besser als if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;} um die Verwendung des aufrufenden Codes zu ermöglichen errno an int außer Reichweite. Das gleiche für if (l < INT_MIN....

    – chux – Wiedereinsetzung von Monica

    22. September 2018 um 17:44 Uhr


AnT steht mit Russlands Benutzer-Avatar
AnT steht zu Russland

Verwenden Sie keine Funktionen von ato... Gruppe. Diese sind kaputt und praktisch unbrauchbar. Eine mäßig bessere Lösung wäre zu verwenden sscanfobwohl es auch nicht perfekt ist.

Um String in Integer umzuwandeln, funktioniert from strto... Gruppe verwendet werden soll. In Ihrem speziellen Fall wäre es strtol Funktion.

  • sscanf hat tatsächlich ein undefiniertes Verhalten, wenn es versucht, eine Zahl außerhalb des Bereichs seines Typs zu konvertieren (z. B. sscanf("999999999999999999999", "%d", &n)).

    – Keith Thompson

    11. August 2011 um 6:50 Uhr

  • @Keith Thompson: Genau das meine ich. atoi liefert kein sinnvolles Erfolgs-/Fehler-Feedback und hat ein undefiniertes Verhalten bei Überlauf. sscanf bietet eine Art Erfolgs-/Fehlerrückmeldung (der Rückgabewert, der es “mäßig besser” macht), hat aber immer noch ein undefiniertes Verhalten beim Überlauf. Nur strtol ist eine gangbare Lösung.

    – AnT steht zu Russland

    11. August 2011 um 6:55 Uhr


  • Einverstanden; Ich wollte nur das potenziell fatale Problem mit betonen sscanf. (Obwohl ich gestehe, dass ich manchmal benutze atoinormalerweise für Programme, von denen ich nicht erwarte, dass sie länger als 10 Minuten überleben, bevor ich die Quelle lösche.)

    – Keith Thompson

    11. August 2011 um 6:58 Uhr

Benutzeravatar von GrandMarquis
GrandMarquis

Du kannst codieren atoi() zum Spass:

int my_getnbr(char *str)
{
  int result;
  int puiss;

  result = 0;
  puiss = 1;
  while (('-' == (*str)) || ((*str) == '+'))
  {
      if (*str == '-')
        puiss = puiss * -1;
      str++;
  }
  while ((*str >= '0') && (*str <= '9'))
  {
      result = (result * 10) + ((*str) - '0');
      str++;
  }
  return (result * puiss);
}

Sie können es auch rekursiv machen, was in 3 Zeilen gefaltet werden kann.

Benutzeravatar von Biswajit Karmakar
Biswajit Karmakar

int atoi(const char* str){
    int num = 0;
    int i = 0;
    bool isNegetive = false;
    if(str[i] == '-'){
        isNegetive = true;
        i++;
    }
    while (str[i] && (str[i] >= '0' && str[i] <= '9')){
        num = num * 10 + (str[i] - '0');
        i++;
    }
    if(isNegetive) num = -1 * num;
    return num;
}

Benutzeravatar von Jacob
Jacob

Ich wollte nur eine Lösung für unsigned long teilen.

unsigned long ToUInt(char* str)
{
    unsigned long mult = 1;
    unsigned long re = 0;
    int len = strlen(str);
    for(int i = len -1 ; i >= 0 ; i--)
    {
        re = re + ((int)str[i] -48)*mult;
        mult = mult*10;
    }
    return re;
}

Benutzeravatar von Lundin
Lundin

Wie bereits erwähnt, die atoi Funktionsfamilie sollten niemals in einem C-Programm verwendet werden, da sie keine Fehlerbehandlung haben.

Die … Die strtol Die Funktionsfamilie ist zu 100 % gleichwertig, jedoch mit erweiterter Funktionalität: Sie verfügt über eine Fehlerbehandlung und unterstützt auch andere Basen als Dezimalzahlen, wie z. B. Hex oder Binär. Daher lautet die richtige Antwort: verwenden strtol (Familie).

Wenn Sie aus irgendeinem Grund darauf bestehen, diese Funktion selbst manuell auszurollen, sollten Sie versuchen, etwas Ähnliches zu tun strtol falls andere Symbole als das optionale Zeichen und die Ziffern vorhanden sind. Es ist durchaus üblich, dass wir beispielsweise Zahlen konvertieren möchten, die Teil einer größeren Zeichenfolge sind.

Eine naive Version mit Fehlerbehandlungsunterstützung könnte wie im folgenden Beispiel aussehen. Dieser Code ist nur für Dezimalzahlen auf Basis 10, verhält sich aber ansonsten wie strtol mit einem optionalen Zeigersatz, der auf das erste gefundene ungültige Symbol zeigt (falls vorhanden). Beachten Sie auch, dass dieser Code keine Überläufe behandelt.

#include <ctype.h>

long my_strtol (char* restrict src, char** endptr)
{
  long result=0;
  long sign=1;

  if(endptr != NULL) 
  {
    /* if input is ok and endptr is provided, 
       it will point at the beginning of the string */
    *endptr = src;
  }

  if(*src=='-')
  {
    sign = -1;
    src++;
  }

  for(; *src!='\0'; src++)
  {
    if(!isdigit(*src)) // error handling
    {
      if(endptr != NULL)
      {
        *endptr = src;
      }
      break;
    }
    result = result*10 + *src - '0';
  }

  return result * sign;
}

Um Überläufe zu handhaben, kann man beispielsweise Code hinzufügen, der die Zeichen zählt und überprüft, dass sie nie über 10 hinausgehen, vorausgesetzt, 32 Bit long das kann max 214748364710 Ziffern.

  • (Ich komme sehr spät zur Party, aber hier wird so viel schlechter Code gepostet. Wenn Sie dies manuell ausrollen, machen Sie zumindest nichts schlechter als atoi.)

    – Ludin

    13. Dezember 2021 um 14:44 Uhr

1427110cookie-checkWie konvertiert man eine Zeichenfolge in C in eine Ganzzahl?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy