Number-Parsing

Die Programmargumente in argv sind stets Strings, oftmals möchte man diese aber als numerische Werte betrachten. Im Folgenden wollen wir uns deshalb mit Funktionen beschäftigen, die eine entsprechende Konvertierung verlässlich ermöglichen.

  1. Warum ist die Verwendung der Funktionen atol/atoi/atof zu diesem Zweck problematich? Ziehen Sie auch die Man-Pages heran.

    Lösung

    Bei diesen Funktionen ist es nicht möglich, Fehler (z.B. invalide Zeichen) zu erkennen — im Fehlerfall geben diese Funktionen 0 zurück, was auch eine korrekte Eingabe sein kann.

  2. Betrachten Sie nun die Funktionen strtol/strtoul/strtod, welche die Parameter const char* nptr und char** endptr entgegen nehmen. Welche Bedeutung haben diese?

    Lösung

    nptr ist der String, der konvertiert werden soll.

    endptr ist ein Ausgabeparameter. An das Ziel des Pointers wird ein Pointer auf den ersten char in nptr gesetzt, der nicht konvertiert werden konnte. (Wenn endptr == NULL, wird nichts geschrieben.)

    Beispiel:

    const char* str = "123x9";
    char* endptr; // uninitialized, value set by strtol
    long res = strtol(str, &endptr, /*base=*/10);
    
    // result of conversion until first invalid character
    assert(res == 123);
    // endptr points to the first invalid character, 'x'.
    assert(endptr == str + 3);
    
  3. Überlegen Sie sich, wie Sie folgende Fehlerfälle erkennen können:

    • Der String enthält für eine Zahl ungültige Zeichen.

      Lösung

      Wenn endptr nicht auf das \0-Terminal zeigt oder auf den Anfang des Strings (der String könnte gar keine Zahl enthalten), war die Konvertierung nicht gänzlich erfolgreich.

    • Die im String enthaltene Zahl passt nicht in den Wertebereich des Ziel-Datentyps.

      Lösung

      In diesem Fall wird errno auf ERANGE gesetzt. Da errno im Erfolgsfall nicht gesetzt wird, muss vor der Konvertierung errno = 0 gesetzt werden.

  4. Passen Sie folgendes C-Programm so an, dass die Paramter in argv der Form <double>,<double> mittels strtod in zwei double-Werte aufgeteilt wird. Nutzen Sie endptr, um den Beginn des zweiten Wertes zu finden. Falls ein Parameter fehlerhaft ist, soll error ausgegeben werden und mit dem nächsten Argument fortgefahren werden.

    #include <math.h>
    #include <stdio.h>
    
    
    int main(int argc, char** argv) {
      for (int i = 1; i < argc; i++) {
        double x = 0, y = 0; // TODO: parse from argv[i]
        printf("%f\n", pow(x, y));
      }
      return 0;
    }
    

    Beispielaufruf:

    $ ./strtonum 2,4 2,2e10 1x5,4 3, 2 "3, 4" .5,1e400 0x1.8p4,2
    16.000000
    inf
    error
    error
    error
    81.000000
    error
    576.000000
    
    Lösung
    #include <errno.h>
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char** argv) {
      for (int i = 1; i < argc; i++) {
        char *endx, *endy;
        errno = 0;
        double x = strtod(argv[i], &endx);
        if (endx == argv[i] || *endx != ',' || errno == ERANGE) {
        error:
          puts("error");
          continue;
        }
    
        double y = strtod(endx + 1, &endy);
        if (endy == endx + 1 || *endy != '\0' || errno == ERANGE)
          goto error;
    
        printf("%f\n", pow(x, y));
      }
      return 0;
    }