docjunior.de
News
20.03.2013
Habe einen UART auf dem Tiny25 gebraucht. Gab keinen. Hab mir selber einen gebaut.

14.09.2011
Ich habe mich entschieden euch ein Geheimnis zu verraten: Ich hab ein Buch geschrieben: Mikrocontroller in Kürze

Menu
zurück
Gästebuch
eM@il
Sitemap
Logindaten
Benutzername

Passwort


Sie sind der
60678. Besucher
home/ Programmierung/ C-PlusPlus/ Commandline Parsing/
 
Index    Aller Anfang
   Die Parameternamen
   dispatch
   Reagieren auf den Befehl
   Das ganze Programm
   Noch ein wenig mehr
   Optionen
   Am Ende

Aller Anfang Lange hab ich schon vor gehabt, aaber mir fiel nichts ein, was ich als Beispiel für dieses Tutorial nutzen konnte. Gestern dann hatte ich die Eingebung: Ich schreibe einen Taschenrechner.
Das Erste, worüber man sich Gedanken machen muss, ist die Steuerung des Produkts. Wen es interessiert, die UML ist mit ihren Use Cases da sehr hilfreich.

Mein Taschenrechner kann die vier Grundrechenarten und den Modulo- der Einfachheit halber ist auch alles in integer gehalten. Diese Rechnungen dürfen jetzt auch als erster Parameter an das Programm übergeben werden. Im Anschluss kommt dann noch die Liste von Operanden, die verrechnet wird. Ich hab sie nicht auf 2 begrenzt, man kann also immer noch eins dazu addieren..

Der Aufruf sieht also wie Folgt aus:
mycalc OPERATOR [OPERAND1 .. OPERANDN]


Die Parameternamen Die Parameter sind also wie folgt benannt.
add, sub, mul, div, mod und help
wobei help einen kurzen Hilfetext ausgibt.

Unser Programm muss diese kennen. Wir schreiben sie uns in eine Liste:
char const *params[] = { "add", "sub", "mul", "div", "mod", "help" };

Der große Vorteil einer solchen liste ist, dass jedes Listenelement über den Index eindeutig identifizierbar ist- zumindest für den Computer. Für den Programmierer ist das nicht soo lesbar. Dem helfen wir ganz dreist ab, indem wir analog zu dieser Liste eine Enumeration einrichten.

enum e_order {
ADD, SUB, MUL, DIV, MOD, HELP
};

Ein weiterer Vorteil der Liste ist der, dass man in einer Schleife hindurch iterieren kann. Das ist auch der Knackpunkt des Ganzen. Wir schreiben uns eine Funktion dispatch(), die durch unsere Parameterliste wandert und einen übergebenen Parameter darin sucht. Wurde dieser gefunden, gibt das Programm dessen Index zurück.

Der Aufruf von dispatch() im Hauptprogramm sieht so aus:
enum e_order order = dispatch(argv[1], paramlist, CNT_PARAM);

Wichtig: argv[0] ist immer der Name des Programms. Der erste Parameter ist demnach argv[1].


dispatch int dispatch(char const *param, char const **list, int data_cnt) {
    int i;

    /* Für jeden möglichen Parameter wird ein Stringvergleich durchgeführt
     */
    for (i = 0; i < data_cnt; i++) {
        if (strcmp(param, list[i]) == 0) {
            return i;
        }
    }
    return -1;
}


Reagieren auf den Befehl Jetzt bleibt uns eigentlich nur noch, auf den zurückgegebenen Befehl zu reagieren.
In einer simplen switch()-Anweisung führen wir nun die entsprechende Aktion aus. Die Enumeration hilft uns dabei, die Lesbarkeit des Programms zu erhalten.

switch (order) {
case ADD:
erg = erg + param;
break;
...
}


Das ganze Programm /*
 * calc.c
 *
 *  Created on: 01.04.2009
 *      Author: tecdroid
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Dies beschreibt, wie das Programm genutzt wird
 */
#define USAGE "usage: mycalc order [operand1 .. operandn]n" 
              "POSSIBLE ORDERSn" 
              "help - this textn" 
              "add - additionn" 
              "sub - substractionn" 
              "mul - multiplicationn" 
              "div - divisionn" 
              "mod - modulonn" 
              "Orders have to be numeric.n"

/* Dies vereinfacht die Änderung der Parameter
 */
#define CNT_PARAM 6

/* Die Parameter für den String-Vergleich
 */
char const *paramlist[] = { "add""sub""mul""div""mod""help" };

/* Die Enumeration ermöglicht die Abfrage per switch-Anweisung
 */
enum e_order {
    ADD, SUB, MUL, DIV, MOD, HELP
};

/*
 * Parameter finden
 */
int dispatch(char const *param, char const **list, int data_cnt) {
    int i;

    /* Für jeden möglichen Parameter wird ein Stringvergleich durchgeführt
     */
    for (i = 0; i < data_cnt; i++) {
        if (strcmp(param, list[i]) == 0) {
            return i;
        }
    }
    return -1;
}

int main(int argc, char **argv) {
    /* Instanziieren und vorbelegen des Befehls
     */
    enum e_order order = HELP;

    /* Das spätere Rechnungsergebnis
     */
    int erg = 0;

    /* Der aktuell zu verarbeitende Operand
     */
    int param = 0;

    /* Schleifenzähler für die Parameter
     */
    int i;

    /* Wenn keine Parameter übergeben worden sind
     * wird angenommen, das der Defaultwert gilt (HELP)
     * Ansonsten wird der Befehl analysiert.
     */
    if (argc > 1) {
        order = dispatch(argv[1], paramlist, CNT_PARAM);
    }

    /*
     * Hilfetext ausgeben, wenn notwendig
     */
    if (order == HELP || argc <= 2) {
        printf(USAGE);
        exit(0);
    }

    /*
     * den ersten numerischen Parameter holen
     */
    erg = atoi(argv[2]);

    /* Die Rechnung durchführen
     */
    for (i = 3; i < argc; i++) {
        param = atoi(argv[i]);

        /* Auswertung des Parameters und Durchführung der Teilrechnung
         */
        switch (order) {
            case ADD:
                erg = erg + param;
                break;
            case SUB:
                erg = erg - param;
                break;
            case MUL:
                erg = erg * param;
                break;
            case DIV:
                erg = erg / param;
                break;
            case MOD:
                erg = erg % param;
                break;

            default:
                printf(USAGE);
                return 0;
        }
    }

    /* Ausgeben des Ergebnisses
     */
    printf("%dn", erg);



Noch ein wenig mehr Man möge mir verzeihen, dass das Programm nicht sonderlich elegant ist. Einiges hätte ich lieber anders gelöst, das wäre aber dann doch eher verwirrend für den Leser gewesen.

Möchte man mehrere Optionen angeben deren Reihenfolge beliebig ist, reicht es aus, eine Schleife über sämtliche Parameter zu machen. Die Switch-Anweisung befüllt dann steuernde Variablen des Programms. Im Anschluss an das Parsings wird die eigentliche Funktion des Programms ausgeführt.


Optionen for (i = 1; i < argc; i++) {
    order = dispatch (argv[i], **paramlist, CNT_PARAM);
    
    switch (order) {
        case DOUBLE: /* Parameter mit Wertzuweisung (z.B. --port 1000) */
            /* den nächsten Parameter holen */
            i++;
            /* und als Wert speichern */
            port = atoi(argv[i]);
            break;
        case SINGLE: /* boolescher Parameter (z.B. --verbose) */
            verbose = true;
            break;
        default: /* Fehlerhafte Parameterübergabe wird mit der Anleitung bestraft */    
            printf(USAGE);
            exit(0);            
    }

do_program();


Am Ende Ich hoffe, dass ich euch hiermit einen Einblick in eine relativ einfache Methode zum Commandline Parsing zeigen konnte.

Wie immer übernehme ich keinerlei Verantwortung dafür, dass alles so funktioniert und ihr damit nicht euren Rechner zerschießt ;)
Ansonsten bin ich gern bereit, eure Fragen zu beantworten.


cmsJr by Jens Rapp, 2006 - 2008