Analysieren von Befehlszeilenargumenten in C?


98

Ich versuche ein Programm zu schreiben, das zwei Dateien Zeile für Zeile, Wort für Wort oder Zeichen für Zeichen in C vergleichen kann. Es muss in der Lage sein, Befehlszeilenoptionen einzulesen -l -w -i or --...

  • Wenn die Option -l ist, werden die Dateien Zeile für Zeile verglichen.
  • Wenn die Option -w ist, werden die Dateien Wort für Wort verglichen.
  • Wenn die Optionen aktiviert sind, wird automatisch davon ausgegangen, dass das nächste Argument der erste Dateiname ist.
  • Wenn die Option -i ist, werden sie ohne Berücksichtigung der Groß- und Kleinschreibung verglichen.
  • Standardmäßig werden die Dateien zeichenweise verglichen.

Es spielt keine Rolle, wie oft die Optionen eingegeben werden, solange -w und -l nicht gleichzeitig eingegeben werden und nicht mehr oder weniger als 2 Dateien vorhanden sind.

Ich weiß nicht einmal, wo ich mit dem Parsen der Befehlszeilenargumente beginnen soll. BITTE HILFE :(

Das ist also der Code, den ich mir für alles ausgedacht habe. Ich habe den Fehler noch nicht überprüft, aber ich habe mich gefragt, ob ich die Dinge zu kompliziert schreibe.

/*
 * Functions to compare files.
 */
int compare_line();
int compare_word();
int compare_char();
int case_insens();

/*
 * Program to compare the information in two files and print message saying 
 * whether or not this was successful.
 */
int main(int argc, char* argv[])
{
/*Loop counter*/
  size_t i = 0;

  /*Variables for functions*/
  int caseIns = 0;
  int line = 0;
  int word = 0;

  /*File pointers*/
  FILE *fp1, *fp2;

  /*
   * Read through command-line arguments for options.
   */
  for (i = 1; i < argc; i++) {
    printf("argv[%u] = %s\n", i, argv[i]);
    if (argv[i][0] == '-') {
       if (argv[i][1] == 'i') 
       {
           caseIns = 1;
       }
       if (argv[i][1] == 'l')
       {
           line = 1;
       }
       if (argv[i][1] == 'w')
       {
           word = 1;
       }
       if (argv[i][1] == '-')
       {
           fp1 = argv[i][2];
           fp2 = argv[i][3];
       }
       else 
       {
           printf("Invalid option.");
           return 2;
       }
    } else {
       fp1(argv[i]);
       fp2(argv[i][1]);
    }
  }

  /*
   * Check that files can be opened.
   */
  if(((fp1 = fopen(fp1, "rb")) ==  NULL) || ((fp2 = fopen(fp2, "rb")) == NULL))
  {
      perror("fopen()");
      return 3;
  }
  else{
        if (caseIns == 1)
        {
            if(line == 1 && word == 1)
            {
                printf("That is invalid.");
                return 2;
            }
            if(line == 1 && word == 0)
            {
                if(compare_line(case_insens(fp1, fp2)) == 0)
                        return 0;
            }
            if(line == 0 && word == 1)
            {
                if(compare_word(case_insens(fp1, fp2)) == 0)
                    return 0;
            }
            else
            {
                if(compare_char(case_insens(fp1,fp2)) == 0)
                    return 0;
            }
        }
        else
        {
            if(line == 1 && word == 1)
            {
                printf("That is invalid.");
                return 2;
            }
            if(line == 1 && word == 0)
            {
                if(compare_line(fp1, fp2) == 0)
                    return 0;
            }
            if(line == 0 && word == 1)
            {
                if(compare_word(fp1, fp2) == 0)
                    return 0;
            }
            else
            {
                if(compare_char(fp1, fp2) == 0)
                    return 0;
            }
        }

  }
    return 1;
    if(((fp1 = fclose(fp1)) == NULL) || (((fp2 = fclose(fp2)) == NULL)))
        {
            perror("fclose()");
            return 3;
        }
        else
        {
            fp1 = fclose(fp1);
            fp2 = fclose(fp2);
        }
}

/*
 * Function to compare two files line-by-line.
 */
int compare_line(FILE *fp1, FILE *fp2)
{
    /*Buffer variables to store the lines in the file*/
    char buff1 [LINESIZE];
    char buff2 [LINESIZE];

    /*Check that neither is the end of file*/
    while((!feof(fp1)) && (!feof(fp2)))
    {
        /*Go through files line by line*/
        fgets(buff1, LINESIZE, fp1);
        fgets(buff2, LINESIZE, fp2);
    }
    /*Compare files line by line*/
    if(strcmp(buff1, buff2) == 0)
    {
        printf("Files are equal.\n");
        return 0;
    }
    printf("Files are not equal.\n");
    return 1;
}   

/*
 * Function to compare two files word-by-word.
 */
int compare_word(FILE *fp1, FILE *fp2)
{
    /*File pointers*/
    FILE *fp1, *fp2;

    /*Arrays to store words*/
    char fp1words[LINESIZE];
    char fp2words[LINESIZE];

    if(strtok(fp1, " ") == NULL || strtok(fp2, " ") == NULL)
    {
        printf("File is empty. Cannot compare.\n");
        return 0;
    }
    else
    {
        fp1words = strtok(fp1, " ");
        fp2words = strtok(fp2, " ");

        if(fp1words == fp2words)
        {
            fputs(fp1words);
            fputs(fp2words);
            printf("Files are equal.\n");
            return 0;
        }
    }
    return 1;
}

/*
 * Function to compare two files character by character.
 */
int compare_char(FILE *fp1,FILE *fp2)
{
    /*Variables to store the characters from both files*/
    int c;
    int d;

    /*Buffer variables to store chars*/
    char buff1 [LINESIZE];
    char buff2 [LINESIZE];

    while(((c = fgetc(fp1))!= EOF) && (((d = fgetc(fp2))!=EOF)))
    {
        if(c == d)
        {
            if((fscanf(fp1, "%c", buff1)) == (fscanf(fp2, "%c", buff2)))
            {
                printf("Files have equivalent characters.\n");
                return 1;
                break;
            }
        }

    }
        return 0;
}

/*
 * Function to compare two files in a case-insensitive manner.
 */
int case_insens(FILE *fp1, FILE *fp2, size_t n)
{
    /*Pointers for files.*/
    FILE *fp1, *fp2;

    /*Variable to go through files.*/
    size_t i = 0;

    /*Arrays to store file information.*/
    char fp1store[LINESIZE];
    char fp2store[LINESIZE];

    while(!feof(fp1) && !feof(fp2))
    {
         for(i = 0; i < n; i++)
         {
                fscanf(fp1, "%s", fp1store);
                fscanf(fp2, "%s", fp2store);

                fp1store = tolower(fp1store);
                fp2store = tolower(fp2store);

                return 1;
         }
    }
    return 0;
}

Ich bin mir nicht ganz sicher, wie ich getopt () verwenden soll ... Ich habe es in meiner Klasse noch nicht gelernt.
user1251020

3
Lesen Sie also die Handbuchseite. Es ist nicht sehr komplex und die Handbuchseite enthält wahrscheinlich ein Beispiel, mit dem Sie experimentieren können (und wenn Ihre lokale Manpage dies nicht tut, können Sie sicherlich Beispiele im Web finden).
Jonathan Leffler

1
Dies ist eine Bibliothek auf hoher Ebene: argparse in c, sehr einfach zu bedienen.
Cofyc


Für einfache Dinge können Sie einfach Ihre eigenen rollen, anstatt eine Bibliothek zu verwenden. Ich habe hier ein Tutorial für den Einstieg geschrieben. Engineeringterminal.com/computer-science/tutorials/…
nalyd88

Antworten:


186

Meines Wissens sind die drei beliebtesten Methoden zum Parsen von Befehlszeilenargumenten in C:

  • Getopt ( #include <unistd.h>aus der POSIX C-Bibliothek), mit dem einfache Aufgaben zum Parsen von Argumenten gelöst werden können . Wenn Sie mit Bash ein wenig vertraut sind, basiert das in Bash integrierte getopt auf Getopt aus der GNU libc.
  • Argp ( #include <argp.h>aus der GNU C Library), das komplexere Aufgaben lösen kann und sich um Dinge kümmert, wie zum Beispiel:
    • -?, --helpfür Hilfemeldung , einschließlich E-Mail-Adresse
    • -V, --versionfür Versionsinformationen
    • --usagefür Nutzungsnachricht
  • Machen Sie es selbst , was ich nicht für Programme empfehle, die jemand anderem gegeben würden, da zu viel schief gehen oder die Qualität beeinträchtigen könnte. Der weit verbreitete Fehler, '-' zu vergessen, um das Parsen von Optionen zu stoppen, ist nur ein Beispiel.

Die Dokumentation zur GNU C-Bibliothek enthält einige schöne Beispiele für Getopt und Argp.

Beispiel für die Verwendung von Getopt

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    bool isCaseInsensitive = false;
    int opt;
    enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;

    while ((opt = getopt(argc, argv, "ilw")) != -1) {
        switch (opt) {
        case 'i': isCaseInsensitive = true; break;
        case 'l': mode = LINE_MODE; break;
        case 'w': mode = WORD_MODE; break;
        default:
            fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    // Now optind (declared extern int by <unistd.h>) is the index of the first non-option argument.
    // If it is >= argc, there were no non-option arguments.

    // ...
}

Beispiel für die Verwendung von Argp

#include <argp.h>
#include <stdbool.h>

const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<your@email.address>";
static char doc[] = "Your program description.";
static char args_doc[] = "[FILENAME]...";
static struct argp_option options[] = { 
    { "line", 'l', 0, 0, "Compare lines instead of characters."},
    { "word", 'w', 0, 0, "Compare words instead of characters."},
    { "nocase", 'i', 0, 0, "Compare case insensitive instead of case sensitive."},
    { 0 } 
};

struct arguments {
    enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode;
    bool isCaseInsensitive;
};

static error_t parse_opt(int key, char *arg, struct argp_state *state) {
    struct arguments *arguments = state->input;
    switch (key) {
    case 'l': arguments->mode = LINE_MODE; break;
    case 'w': arguments->mode = WORD_MODE; break;
    case 'i': arguments->isCaseInsensitive = true; break;
    case ARGP_KEY_ARG: return 0;
    default: return ARGP_ERR_UNKNOWN;
    }   
    return 0;
}

static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };

int main(int argc, char *argv[])
{
    struct arguments arguments;

    arguments.mode = CHARACTER_MODE;
    arguments.isCaseInsensitive = false;

    argp_parse(&argp, argc, argv, 0, 0, &arguments);

    // ...
}

Beispiel für das Selbermachen

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{   
    bool isCaseInsensitive = false;
    enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
    size_t optind;
    for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) {
        switch (argv[optind][1]) {
        case 'i': isCaseInsensitive = true; break;
        case 'l': mode = LINE_MODE; break;
        case 'w': mode = WORD_MODE; break;
        default:
            fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
            exit(EXIT_FAILURE);
        }   
    }   

    // *argv points to the remaining non-option arguments.
    // If *argv is NULL, there were no non-option arguments.

    // ...
}   

Haftungsausschluss: Ich bin neu in Argp, das Beispiel könnte Fehler enthalten.


9
Wirklich gründliche Antwort, danke Christian (positiv bewertet). Mac-Benutzer sollten sich jedoch bewusst sein, dass der Argp-Ansatz nicht plattformübergreifend kompatibel ist. Wie ich hier gefunden habe , ist Argp eine nicht standardisierte Glibc-API-Erweiterung. Es ist in gnulib verfügbar und kann daher explizit zu einem Projekt hinzugefügt werden. Für Entwickler nur für Macs oder plattformübergreifende Entwickler ist es jedoch wahrscheinlich einfacher, den getopt-Ansatz zu verwenden.
thclark

1
Für die Do-it-yourself-Version gefällt mir nicht, dass die Optionen später zusätzlichen Text zulassen, wie z. B. -wzzz, analysiert das gleiche wie -w, und dass die Optionen vor den Dateiargumenten stehen müssen.
Jake

1
@ Jake du hast recht. Respekt dafür, dass du das entdeckst. Ich erinnere mich nicht, ob ich das entdeckt habe, als ich es geschrieben habe. Es ist wieder ein perfektes Beispiel dafür, dass DIY so leicht falsch zu verstehen ist und daher nicht gemacht werden sollte. Vielen Dank für die Nachricht, ich könnte das Beispiel beheben.
Christian Hujer

18

Verwenden Sie getopt()oder vielleicht getopt_long().

int iflag = 0;
enum { WORD_MODE, LINE_MODE } op_mode = WORD_MODE;  // Default set
int opt;

while ((opt = getopt(argc, argv, "ilw") != -1)
{
    switch (opt)
    {
    case 'i':
        iflag = 1;
        break;
    case 'l':
        op_mode = LINE_MODE;
        break;
    case 'w':
        op_mode = WORD_MODE;
        break;
    default:
        fprintf(stderr, "Usage: %s [-ilw] [file ...]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
}

/* Process file names or stdin */
if (optind >= argc)
    process(stdin, "(standard input)", op_mode);
else
{
    int i;
    for (i = optind; i < argc; i++)
    {
        FILE *fp = fopen(argv[i], "r");
        if (fp == 0)
            fprintf(stderr, "%s: failed to open %s (%d %s)\n",
                    argv[0], argv[i], errno, strerror(errno));
        else
        {
            process(fp, argv[i], op_mode);
            fclose(fp);
        }
    }
 }

Beachten Sie, dass Sie bestimmen müssen, welche Header enthalten sein sollen (ich mache es 4, die erforderlich sind), und die Art und Weise, wie ich den op_modeTyp geschrieben habe, bedeutet, dass Sie ein Problem in der Funktion haben process()- Sie können dort unten nicht auf die Aufzählung zugreifen. Es ist am besten, die Aufzählung außerhalb der Funktion zu verschieben. Sie können sogar op_modeeine Dateibereichsvariable ohne externe Verknüpfung erstellen (eine ausgefallene Art zu sagen static), um zu vermeiden, dass sie an die Funktion übergeben wird. Dieser Code ist kein -Synonym für Standardeingabe, eine weitere Übung für den Leser. Beachten Sie, dass getopt()automatisch --das Ende der Optionen für Sie markiert wird.

Ich habe keine Version der obigen Eingabe hinter einem Compiler ausgeführt. Es könnte Fehler geben.


Schreiben Sie für zusätzliche Gutschriften eine (Bibliotheks-) Funktion:

int filter(int argc, char **argv, int idx, int (*function)(FILE *fp, const char *fn));

Dies kapselt die Logik für die Verarbeitung von Dateinamenoptionen nach der getopt()Schleife. Es sollte -als Standardeingabe behandelt werden. Beachten Sie, dass die Verwendung dieser Option darauf hinweist, dass es op_modesich um eine statische Dateibereichsvariable handeln sollte. Die filter()Funktion übernimmt argc, argv, optindund ein Zeiger auf die Verarbeitungsfunktion. Es sollte 0 (EXIT_SUCCESS) zurückgeben, wenn es in der Lage war, alle Dateien und alle Aufrufe der Funktion 0 zu öffnen, andernfalls 1 (oder EXIT_FAILURE). Eine solche Funktion vereinfacht das Schreiben von Unix-Filterprogrammen, die in der Befehlszeile oder in der Standardeingabe angegebene Dateien lesen.


Ich mag es nicht, dass getopt () keine Optionen nach der ersten Datei zulässt.
Jake

POSIX getopt()nicht; GNU getopt()tut dies standardmäßig. Treffen Sie Ihre Wahl. Ich bin nicht begeistert von den Optionen nach dem Verhalten von Dateinamen, hauptsächlich weil sie plattformübergreifend nicht zuverlässig sind.
Jonathan Leffler

14

Ich habe festgestellt, dass Gengetopt sehr nützlich ist - Sie geben die gewünschten Optionen mit einer einfachen Konfigurationsdatei an und es wird ein .c / .h-Paar generiert, das Sie einfach einschließen und mit Ihrer Anwendung verknüpfen. Der generierte Code verwendet getopt_long, scheint die gängigsten Arten von Befehlszeilenparametern zu verarbeiten und kann viel Zeit sparen.

Eine gengetopt-Eingabedatei könnte ungefähr so ​​aussehen:

version "0.1"
package "myApp"
purpose "Does something useful."

# Options
option "filename" f "Input filename" string required
option "verbose" v "Increase program verbosity" flag off
option "id" i "Data ID" int required
option "value" r "Data value" multiple(1-) int optional 

Das Generieren des Codes ist einfach und spuckt aus cmdline.hund cmdline.c:

$ gengetopt --input=myApp.cmdline --include-getopt

Der generierte Code ist einfach zu integrieren:

#include <stdio.h>
#include "cmdline.h"

int main(int argc, char ** argv) {
  struct gengetopt_args_info ai;
  if (cmdline_parser(argc, argv, &ai) != 0) {
    exit(1);
  }
  printf("ai.filename_arg: %s\n", ai.filename_arg);
  printf("ai.verbose_flag: %d\n", ai.verbose_flag);
  printf("ai.id_arg: %d\n", ai.id_arg);
  int i;
  for (i = 0; i < ai.value_given; ++i) {
    printf("ai.value_arg[%d]: %d\n", i, ai.value_arg[i]);
  }
}

Wenn Sie zusätzliche Überprüfungen durchführen müssen (z. B. sicherstellen, dass sich Flags gegenseitig ausschließen), können Sie dies relativ einfach mit den in der gengetopt_args_infoStruktur gespeicherten Daten tun .


1 ++ außer dass es Code generiert, der Warnungen generiert :(
Katze

Ja, leider. Ich habe Ausnahmen in meine cmake-Datei eingefügt.
DavidA

Ich werde wahrscheinlich nur GCC-Pragmas verwenden, um Warnungen für diese Datei zu ignorieren (schrecklich, ich weiß)
Katze

Beachten Sie, dass Sie sie offensichtlich verlieren, wenn Sie die Quelle neu generieren. Daher möchten Sie sie möglicherweise als Patch in Ihrem Erstellungsprozess anwenden. Ehrlich gesagt fand ich es einfacher, Warnungen für diese spezifischen Dateien einfach auszuschalten.
DavidA

Nun #include, nein, ich meine, die Pragmas um das zu legen , nicht in die generierte Datei selbst. Warnungen auszuschalten ist mir verboten :-)
Katze

6

Ich bin sehr überrascht, dass niemand James Theilers "opt" -Paket angesprochen hat.

Sie finden opt unter http://public.lanl.gov/jt/Software/

und ein schmeichelhafter Beitrag mit einigen Beispielen dafür, wie viel einfacher es ist als andere Ansätze, ist hier:

http://www.decompile.com/not_invented_here/opt/


2
@cat Warum denkst du, dass seitdem ein Update benötigt wird? Das ist einfach die falsche Einstellung zu Software.
Joshua Hedges

@JoshuaHedges Sofern ich das Projekt nicht selbst pflegen möchte, möchte ich aktiv gepflegten Code in meinem aktiv gepflegten Code verwenden. Es gibt viele Projekte aus dem Jahr 2006, die aktiv gepflegt werden, aber dieses ist gestorben und wahrscheinlich mit Fehlern. Außerdem habe ich vor 2 Jahren (fast genau!) Vor langer Zeit Folgendes geschrieben: P
cat

1
opt wird nicht aktiv gewartet, da es vollständig und kompakt ist. Für Kicks habe ich es einfach heruntergeladen und versucht, es zu erstellen (gcc-7.3) und festgestellt, dass die Bibliothek erstellt und funktioniert, aber der C ++ - Test könnte einige kleinere Arbeiten vertragen. iostream.h sollte zu iostream werden und den Namespace std verwenden. sollte hinzugefügt werden. Ich werde es James gegenüber erwähnen. Dies betrifft nur den C ++ - API-Test, nicht den Code selbst.
Markgalassi

4

Docopt hat eine C-Implementierung, die ich sehr nett fand: https://github.com/docopt/docopt.c

Aus einem standardisierten Manpage-Format, das Befehlszeilenoptionen beschreibt, leitet docopt einen Argumentparser ab und erstellt ihn. Dies wurde in Python begonnen; Die Python-Version analysiert buchstäblich nur die Dokumentzeichenfolge und gibt ein Diktat zurück. Dies in C zu tun, erfordert etwas mehr Arbeit, ist jedoch sauber zu verwenden und weist keine externen Abhängigkeiten auf.


3

Es gibt eine großartige Allzweck-C-Bibliothek libUCW, die ordentliches Parsen von Befehlszeilenoptionen und Laden von Konfigurationsdateien umfasst .

Die Bibliothek enthält auch eine gute Dokumentation und einige andere nützliche Dinge (schnelle E / A, Datenstrukturen, Allokatoren, ...), die jedoch separat verwendet werden können.

Beispiel für einen libUCW-Optionsparser (aus den Bibliotheksdokumenten)

#include <ucw/lib.h>
#include <ucw/opt.h>

int english;
int sugar;
int verbose;
char *tea_name;

static struct opt_section options = {
  OPT_ITEMS {
    OPT_HELP("A simple tea boiling console."),
    OPT_HELP("Usage: teapot [options] name-of-the-tea"),
    OPT_HELP(""),
    OPT_HELP("Options:"),
    OPT_HELP_OPTION,
    OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
    OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
    OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
    OPT_STRING(OPT_POSITIONAL(1), NULL, tea_name, OPT_REQUIRED, ""),
    OPT_END
  }
};

int main(int argc, char **argv)
{
  opt_parse(&options, argv+1);
  return 0;
}

Positionsoption hat Fehler. Wenn es zwei OPT_STRING gibt und einer positionell ist, einer nicht, kann er nicht analysiert werden.
NewBee

2

Ich habe eine winzige Bibliothek namens XOpt geschrieben, die Argumente ähnlich wie POpt analysiert, mit denen ich mehrere Probleme hatte . Verwendet das Parsen von Argumenten im GNU-Stil und hat eine sehr ähnliche Oberfläche wie POpt.

Ich benutze es von Zeit zu Zeit mit großem Erfolg und es funktioniert so ziemlich überall.


1

Wenn ich mein eigenes Horn betätigen darf, möchte ich auch vorschlagen, einen Blick auf eine von mir geschriebene Option zum Parsen von Bibliotheken zu werfen: dropt .

  • Es ist eine C-Bibliothek (mit einem C ++ - Wrapper, falls gewünscht).
  • Es ist leicht.
  • Es ist erweiterbar (benutzerdefinierte Argumenttypen können einfach hinzugefügt werden und sind den integrierten Argumenttypen gleichgestellt).
  • Es sollte sehr portabel sein (es ist in Standard C geschrieben) und keine Abhängigkeiten aufweisen (außer der C-Standardbibliothek).
  • Es hat eine sehr uneingeschränkte Lizenz (zlib / libpng).

Eine Funktion, die viele andere nicht bieten, ist die Möglichkeit, frühere Optionen zu überschreiben. Wenn Sie beispielsweise einen Shell-Alias ​​haben:

alias bar="foo --flag1 --flag2 --flag3"

und Sie möchten verwenden, baraber mit --flag1deaktiviert, können Sie Folgendes tun:

bar --flag1=0

0
#include <stdio.h>

int main(int argc, char **argv)
{
    size_t i;
    size_t filename_i = -1;

    for (i = 0; i < argc; i++)
    {
        char const *option =  argv[i];
        if (option[0] == '-')
        {
            printf("I am a flagged option");
            switch (option[1])
            {
                case 'a':
                    /*someting*/
                    break;
                case 'b':
                    break;
                case '-':
                    /* "--" -- the next argument will be a file.*/
                    filename_i = i;
                    i = i + 1;
                    break;
                default:
                    printf("flag not recognised %s", option);
                    break;
            }
        }
        else
        {   
            printf("I am a positional argument");
        }

        /* At this point, if -- was specified, then filename_i contains the index
         into argv that contains the filename. If -- was not specified, then filename_i will be -1*/
     }
  return 0;
}

4
Nein; absolut keine gute Möglichkeit, dies zu tun ... Verwenden Sie eine der Argument-Parsing-Funktionen - getopt()oder getopt_long().
Jonathan Leffler

5
Klingt nach einem Betrug, da dies offensichtlich eine Hausaufgabenfrage ist. Darüber hinaus fällt es dem OP schwer, das Konzept zu verstehen, was eine Zeichenfolge ist und wie Teile davon gelesen werden. Es ist ein Fehler, ihm etwas aufzuschreiben.
Pod

Es ist eine Hausaufgabenfrage. Ich weiß, was eine Zeichenfolge ist. Ich verstehe nur nicht, wie man die Befehlszeilenargumente aufschlüsselt, weil es mir verwirrend erscheint, wenn Sie die Optionen beliebig oft eingeben können, sodass Sie nicht wirklich herausfinden können, wo sich die Dateinamen befinden. Vielleicht überdenke ich es?
user1251020

0

Anweisungsvorlage zum Parsen von Befehlszeilenargumenten in C.

C:> programName -w - fileOne.txt fileTwo.txt

BOOL argLine = FALSE;
BOOL argWord = FALSE;
BOOL argChar = FALSE;
char * fileName1 = NULL;
char * fileName2 = NULL;

int main(int argc, char * argv[]) {
    int i;
    printf("Argument count=%d\n",argc);
    for (i = 0; i < argc; i++) {
        printf("Argument %s\n",argv[i]);
        if (strcmp(argv[i],"-l")==0) {
            argLine = TRUE;
            printf("    argLine=TRUE\n");
        }
        else if (strcmp(argv[i],"-w")==0) {
            argWord = TRUE;
            printf("    argWord=TRUE\n");
        }
        else if (strcmp(argv[i],"-c")==0) {
            argChar = TRUE;
            printf("    argChar=TRUE\n");
        }
        else if (strcmp(argv[i],"--")==0) {
            if (i+1 <= argc) {
                fileName1 = argv[++i];
                printf("    fileName1=%s\n",fileName1);
            }
            if (i+1 <= argc) {
                fileName2 = argv[++i];
                printf("    fileName2=%s\n",fileName2);
            }
        }
    }
    return 0;
}

1
... Ich glaube nicht, dass es in C eine boolesche Variable gibt ...?
user1251020

Meine Eclipse / Windows-Umgebung hat den Typ BOOL. Ändern Sie es einfach in int oder char und passen Sie den Code entsprechend an.
Java42

1
C99 hat _Boolzu jeder Zeit einen Typ und einen Header, <stdbool.h>der boolals _Boolund trueund falseund __bool_true_false_are_definedalle Makros definiert (die ausnahmsweise undefiniert und neu definiert werden können, ohne undefiniertes Verhalten aufzurufen; diese Lizenz ist jedoch als "veraltet" gekennzeichnet). Wenn Sie also einen C99-Compiler haben, können Sie <stdbool.h>und verwenden bool. Wenn nicht, schreiben Sie entweder eine für sich selbst (es ist nicht schwer) oder Sie verwenden ein natives Äquivalent.
Jonathan Leffler

1
@Wolfer Meine C-Umgebung hat den Typ BOOL (als typedef int BOOL) und den Typ boolean (als typedef unsigned char boolean) und keine Definition für den Typ bool. Im Beispiel ändern Sie einfach int oder char und passen den Code entsprechend an.
Java42

3
Ich bin mit diesem Ansatz nicht einverstanden. Verwenden Sie eine Bibliotheksfunktion, um Optionen zu analysieren.
Jonathan Leffler

0
    /*
      Here's a rough one not relying on any libraries.
      Example:
      -wi | -iw //word case insensitive
      -li | -il //line case insensitive
      -- file  //specify the first filename (you could just get the files
      as positional arguments in the else statement instead)
      PS: don't mind the #define's, they're just pasting code :D
    */
    #ifndef OPT_H
    #define OPT_H

    //specify option requires argument
    #define require \
      optarg = opt_pointer + 1; \
      if (*optarg == '\0') \
      { \
        if (++optind == argc) \
          goto opt_err_arg; \
        else \
          optarg = argv[optind]; \
      } \
      opt_pointer = opt_null_terminator;

    //start processing argv
    #define opt \
    int   optind                 = 1; \
    char *opt_pointer            = argv[1]; \
    char *optarg                 = NULL; \
    char  opt_null_terminator[2] = {'\0','\0'}; \
    if (0) \
    { \
      opt_err_arg: \
        fprintf(stderr,"option %c requires argument.\n",*opt_pointer); \
        return 1; \
      opt_err_opt: \
        fprintf(stderr,"option %c is invalid.\n",*opt_pointer); \
        return 1; \
    } \
    for (; optind < argc; opt_pointer = argv[++optind]) \
      if (*opt_pointer++ == '-') \
      { \
        for (;;++opt_pointer) \
          switch (*opt_pointer) \
          {

    //stop processing argv
    #define done \
          default: \
            if (*opt_pointer != '\0') \
              goto opt_err_opt; \
            else \
              goto opt_next; \
            break; \
          } \
        opt_next:; \
      }
    #endif //opt.h

    #include <stdio.h>
    #include "opt.h"
    int
    main (int argc, char **argv)
    {
      #define by_character 0
      #define by_word      1
      #define by_line      2
      int cmp = by_character;
      int case_insensitive = 0;
      opt
      case 'h':
        puts ("HELP!");
        break;
      case 'v':
        puts ("fileCMP Version 1.0");
        break;
      case 'i':
        case_insensitive = 1;
        break;
      case 'w':
        cmp = by_word;
        break;
      case 'l':
        cmp = by_line;
        break;
      case '-':required
        printf("first filename: %s\n", optarg);
        break;
      done
      else printf ("Positional Argument %s\n", argv[optind]);
      return 0;
    }

2
Sie müssen Ihren Code erklären, anstatt ihn nur hochzuwerfen und zu erwarten, dass jeder ihn versteht. Dies ist eine Seite zum Lernen, nicht nur zum Kopieren und Einfügen.
Yokai

0

Okay, das ist der Beginn einer langen Geschichte - kurz gemacht, um eine Befehlszeile in C zu analysieren ...

/**
* Helper function to parse the command line
* @param argc Argument Counter
* @param argv Argument Vector
* @param prog Program Instance Reference to fill with options
*/
bool parseCommandLine(int argc, char* argv[], DuplicateFileHardLinker* prog) {
  bool pathAdded = false;

  // iterate over all arguments...
  for ( int i = 1; i<argc; i++ ) {

    // is argv a command line option ?
    if ( argv[i][0] == '-' || argv[i][0] == '/' ) {

// ~~~~~~ Optionally Cut that part vvvvvvvvvvvvv for sake of simplicity ~~~~~~~
      // check for longer options
            if ( stricmp( &argv[i][1], "NoFileName"  ) == 0
              ||  strcmp( &argv[i][1], "q1"          ) == 0 ) {

        boNoFileNameLog = true;
      } else if ( strcmp( &argv[i][1], "HowAreYou?"    ) == 0 ) {
          logInfo( "SECRET FOUND: Well - wow I'm glad ya ask me.");
      } else {

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Now here comes the main thing:
//
        // check for one char options
        while ( char option = *++argv[i] ) {

          switch ( option ) {
          case '?':
            // Show program usage

            logInfo(L"Options:");
            logInfo(L"  /q\t>Quite mode");
            logInfo(L"  /v\t>Verbose mode");
            logInfo(L"  /d\t>Debug mode");
            return false;

            // Log options
          case 'q':
            setLogLevel(LOG_ERROR);
            break;

          case 'v':
            setLogLevel(LOG_VERBOSE);
            break;

          case 'd':
            setLogLevel(LOG_DEBUG);
            break;

          default:
            logError(L"'%s' is an illegal command line option!"
                      "  Use /? to see valid options!", option);
            return false;
          } // switch one-char-option
        } //while one-char-options
      }  //else one vs longer options
    } // if isArgAnOption

// 
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^  So that's it! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// What follows now is are some usefull extras...
//
    else {


      // the command line options seems to be a path...
      WCHAR tmpPath[MAX_PATH_LENGTH];
      mbstowcs(tmpPath, argv[i], sizeof(tmpPath));

      // check if the path is existing!
      //...

      prog->addPath(tmpPath); //Comment or remove to get a working example
      pathAdded = true;
    }
  }

  // check for parameters
  if ( !pathAdded ) {
    logError("You need to specify at least one folder to process!\n"
             "Use /? to see valid options!");
    return false;
  }

  return true;
}



int main(int argc, char* argv[]) {

  try {
    // parse the command line
    if ( !parseCommandLine(argc, argv, prog) ) {
      return 1; 
    }

// I know that sample is just to show how the nicely parse commandline Arguments
// So Please excuse more nice useful C-glatter that follows now...
  }
  catch ( LPCWSTR err ) {
    DWORD dwError = GetLastError();
    if ( wcslen(err) > 0 ) {
      if ( dwError != 0 ) {
        logError(dwError, err);
      }
      else {
        logError(err);
      }
    }
    return 2;
  }
}

#define LOG_ERROR               1
#define LOG_INFO                0
#define LOG_VERBOSE             -1
#define LOG_DEBUG               -2

/** Logging Level for the console output */
int logLevel = LOG_INFO;

void logError(LPCWSTR message, ...) {
  va_list argp;
  fwprintf(stderr, L"ERROR: ");
  va_start(argp, message);
  vfwprintf(stderr, message, argp);
  va_end(argp);
  fwprintf(stderr, L"\n");
}


void logInfo(LPCWSTR message, ...) {
  if ( logLevel <= LOG_INFO ) {
    va_list argp;
    va_start(argp, message);
    vwprintf(message, argp);
    va_end(argp);
    wprintf(L"\n");
  }
}

Beachten Sie, dass diese Version auch das Kombinieren von Argumenten unterstützt: Anstatt zu schreiben, funktioniert / h / s -> / hs auch.

Es tut mir leid, dass ich die n-te Person bin, die hier schreibt - aber ich war nicht wirklich zufrieden mit all den Standalone-Versionen, die ich hier gesehen habe. Nun, die lib sind ganz nett. Daher würde ich den libUCW- Optionsparser Arg oder Getopt einem hausgemachten vorziehen .

Beachten Sie, dass Sie Folgendes ändern können:

*++argv[i]-> (++argv*)[0] länger weniger kryptisch aber immer noch kryptisch.

Okay, lassen Sie es uns zusammenfassen: 1. argv [i] -> Zugriff auf das i-te Element im argv-char-Zeigerfeld

  1. ++ * ... -> leitet den argv-Zeiger um ein Zeichen weiter

  2. ... [0] -> folgt dem Zeiger, liest das Zeichen

  3. ++ (...) -> Klammern sind vorhanden, daher erhöhen wir den Zeiger und nicht den Zeichenwert selbst.

So schön, dass in C ## die Zeiger 'gestorben' sind - es lebe die Zeiger !!!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.