String in argv/argc parsen

Lesezeit: 10 Minuten

Benutzeravatar von codebox
Codebox

Gibt es in C eine Möglichkeit, ein Stück Text zu analysieren und Werte für argv und argc zu erhalten, als ob der Text auf der Befehlszeile an eine Anwendung übergeben worden wäre?

Dies muss nicht unter Windows funktionieren, sondern nur unter Linux – es ist mir auch egal, Argumente zu zitieren.

  • Für welche Plattform? Wie Befehlszeilen in argc/argv geparst werden, ist beispielsweise zwischen Windows- und UNIX-basierten Systemen ziemlich unterschiedlich. Unter UNIX transformiert die Shell die Befehlszeile normalerweise erheblich, einschließlich Globbing (Dateimustererweiterung) sowie Variablenersetzung. Unter Windows wird die Dateimustererweiterung nicht von der Shell durchgeführt (es sei denn, Sie verwenden natürlich etwas wie Cygwin oder MKS Toolkit).

    – Laurence Gonsalves

    10. November 2009 um 9:18 Uhr

  • Wenn Sie nicht einmal mit Anführungszeichen umgehen müssen, würde ich wirklich vorschlagen, Ihre eigene Funktion zu codieren, anstatt eine Bibliothek eines Drittanbieters nur für diese Aufgabe einzuführen.

    – Remo.D

    10. November 2009 um 10:15 Uhr

  • Hast du es mit getopt() versucht? (Mann 3 getopt). Sie können die meisten UNIX/Linux-Standard-Tool-Quellen für Beispiele sehen, eine RIESIGE Anzahl davon. Sogar die Manpage (zumindest Linux) enthält ein anständiges Beispiel. Es gibt auch eine Reihe von Wrappern (Sie sehen hier Empfehlungen), aber getopt() scheint der einzige zu sein, der für JEDE UNIX-Plattform verfügbar ist (eigentlich scheint es Teil des POSIX-Standards zu sein).

    – Roman Nikitschenko

    10. November 2009 um 11:26 Uhr

  • Wenn Sie immer noch interessiert sind und industrielle Stärke von Grund auf neu wollen, in einem kleinen Codepaket. Suchen Sie auf dieser Seite nach nargv Bei weitem die beste Lösung, die ich hier aus reinem C-Code gesehen habe. Bitte stimmen Sie diese Antwort ab! Damit andere es finden können.

    user735796

    9. April 2012 um 10:32 Uhr

  • @ user735796 Ich habe gesucht nargv und dein Kommentar ist der einzige Hit. Also googelte ich: github.com/hypersoft/nargv … Einige Kommentare. Dies verwendet C99 und funktioniert daher nicht mit dem Microsoft C-Compiler. Es ist auch eine Idee, Komponententests mit einer Reihe von Testfällen zu haben, die jede Art von Szenario für den Parser überprüfen, um zu überprüfen, ob es wie erwartet funktioniert.

    – Joakim

    1. März 2015 um 11:14 Uhr

Ich bin überrascht, dass niemand die einfachste Antwort mit der Standard-POSIX-Funktionalität gegeben hat:

http://www.opengroup.org/onlinepubs/9699919799/functions/wordexp.html

  • Das kann mehr bewirken, als Sie wollen. ZB führt es Erweiterungen von Shell-Wörtern durch, einschließlich der Ersetzung von Umgebungsvariablen, z. B. der Ersetzung $PATH mit dem aktuellen Pfad.

    – Craig McQueen

    3. April 2013 um 0:57 Uhr

  • Ich denke, es hängt davon ab, was Sie mit parse in argc/argv meinen; Sicherlich beinhaltet dies einiges von dem, was die Shell tut (Verarbeitung von Zitaten), aber die Erweiterung von Variablen und andere Dinge sind fragwürdiger. Übrigens wordexp hat eine Option zum Deaktivieren der Befehlserweiterung.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    3. April 2013 um 1:49 Uhr

  • Wenn du meinst WRDE_NOCMDdas scheint die Expansion nicht zu verhindern $PATHnoch erweitern * zu den Namen der Dateien im aktuellen Verzeichnis.

    – Craig McQueen

    3. April 2013 um 1:57 Uhr

  • Ich habe nicht gesagt, dass es die Variablenerweiterung verhindert, nur dass eine andere Sache, die Sie vielleicht deaktivieren möchten, die Befehlserweiterung, deaktiviert werden kann.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    3. April 2013 um 2:21 Uhr

  • Das ist genau das, wonach ich gesucht habe und scheint sehr gut zu funktionieren. Ich brauchte es, um einen benutzerdefinierten Befehl an zu übergeben posix_spawn, ohne zu wissen, ob es weitere Argumente geben würde. Ein kurzes Codebeispiel würde diese Antwort jedoch so viel besser machen. Ja, sogar jetzt, mehr als sieben Jahre später. 🙂

    – Domson

    19. Februar 2018 um 20:43 Uhr


Benutzeravatar von Remo.D
Remo.D

Wenn die Glib-Lösung für Ihren Fall zu viel des Guten ist, können Sie erwägen, selbst eine zu programmieren.

Dann kannst du:

  • scannen Sie die Zeichenfolge und zählen Sie, wie viele Argumente es gibt (und Sie erhalten Ihr argc)
  • Weisen Sie ein Array von char * zu (für Ihr argv)
  • Scannen Sie die Zeichenfolge erneut, weisen Sie die Zeiger im zugewiesenen Array zu und ersetzen Sie Leerzeichen durch ‘\0’ (wenn Sie die Zeichenfolge mit den Argumenten nicht ändern können, sollten Sie sie duplizieren).
  • Vergessen Sie nicht, das freizugeben, was Sie zugewiesen haben!

Das folgende Diagramm sollte (hoffentlich) verdeutlichen:

             aa bbb ccc "dd d" ee         <- original string

             aa0bbb0ccc00dd d00ee0        <- transformed string
             |  |   |    |     |
   argv[0] __/  /   /    /     /
   argv[1] ____/   /    /     /
   argv[2] _______/    /     /
   argv[3] ___________/     /
   argv[4] ________________/ 

Eine mögliche API könnte sein:

    char **parseargs(char *arguments, int *argc);
    void   freeparsedargs(char **argv);

Sie müssen zusätzliche Überlegungen anstellen, um freeparsedargs() sicher zu implementieren.

Wenn Ihre Zeichenfolge sehr lang ist und Sie nicht zweimal scannen möchten, können Sie Alternativen in Betracht ziehen, z. B. mehr Elemente für die argv-Arrays zuweisen (und bei Bedarf neu zuweisen).

BEARBEITEN: Vorgeschlagene Lösung (behandelt das zitierte Argument nicht).

    #include <stdio.h>

    static int setargs(char *args, char **argv)
    {
       int count = 0;

       while (isspace(*args)) ++args;
       while (*args) {
         if (argv) argv[count] = args;
         while (*args && !isspace(*args)) ++args;
         if (argv && *args) *args++ = '\0';
         while (isspace(*args)) ++args;
         count++;
       }
       return count;
    }

    char **parsedargs(char *args, int *argc)
    {
       char **argv = NULL;
       int    argn = 0;

       if (args && *args
        && (args = strdup(args))
        && (argn = setargs(args,NULL))
        && (argv = malloc((argn+1) * sizeof(char *)))) {
          *argv++ = args;
          argn = setargs(args,argv);
       }

       if (args && !argv) free(args);

       *argc = argn;
       return argv;
    }

    void freeparsedargs(char **argv)
    {
      if (argv) {
        free(argv[-1]);
        free(argv-1);
      } 
    }

    int main(int argc, char *argv[])
    {
      int i;
      char **av;
      int ac;
      char *as = NULL;

      if (argc > 1) as = argv[1];

      av = parsedargs(as,&ac);
      printf("== %d\n",ac);
      for (i = 0; i < ac; i++)
        printf("[%s]\n",av[i]);

      freeparsedargs(av);
      exit(0);
    }

  • weil getopt einen anderen Job macht. Es nimmt eine Reihe von Argumenten und sucht darin nach Optionen. Bei dieser Frage geht es darum, eine Zeichenfolge von “Argumenten” in ein Array von char * aufzuteilen, was getopt nicht kann

    – Remo.D

    10. November 2009 um 20:28 Uhr

  • Wenn Sie die Eingabezeichenfolge so umwandeln, können Sie keine Zeichenfolgenverkettung mit Anführungszeichen wie “this” oder “this” durchführen. Siehe meine Antwort für eine voll funktionsfähige Lösung.

    user735796

    9. April 2012 um 18:32 Uhr

  • (Spitze im Voraus) Beachten Sie, dass eine kleine Sache fehlt, um mit dem Standard konform zu sein argc/argv layout: Der Eintrag hinter dem letzten gültigen in argv ist immer eingestellt NULL ("foo bar": argv[0] -> "foo", argv[1] -> "bar", argv[2] -> NULL).

    – Max Truxa

    20. Oktober 2014 um 13:11 Uhr


Benutzeravatar von sstteevvee
ssteevvee

Hier mein Beitrag. Es ist schön und kurz, aber Dinge, auf die Sie achten sollten, sind:

  • Die Verwendung von strtok ändert die ursprüngliche “commandLine”-Zeichenfolge und ersetzt die Leerzeichen durch \0-Trennzeichen am Ende der Zeichenfolge
  • argv[] zeigt am Ende auf “commandLine”, also ändern Sie es nicht, bis Sie mit argv fertig sind[].

Der Code:

enum { kMaxArgs = 64 };
int argc = 0;
char *argv[kMaxArgs];

char *p2 = strtok(commandLine, " ");
while (p2 && argc < kMaxArgs-1)
  {
    argv[argc++] = p2;
    p2 = strtok(0, " ");
  }
argv[argc] = 0;

Sie können jetzt argc und argv verwenden oder sie an andere Funktionen übergeben, die wie “foo(int argc, char **argv)” deklariert sind.

  • Danke, das hat etwas Zeit gespart. An alle anderen, die dies verwenden: “char* p1” (obwohl Ihr Compiler Ihnen gesagt hätte =])

    – jrr

    30. September 2013 um 21:02 Uhr

  • Berücksichtigt dies maskierte Argumente wie “some/long path/to file.txt”?

    – Gier

    17. März um 15:19 Uhr

  • Nein, Sie müssten danach selbst nach Angeboten suchen und diese bearbeiten. Wenn Ihr Code auf einem “echten” Betriebssystem ausgeführt wird, würde ich empfehlen, zu sehen, was es bietet. Zum Beispiel die Glib, wie in einer anderen Lösung vorgeschlagen, die Ihnen diese Art von Funktionen bieten sollte.

    – ssteevvee

    19. März um 8:58 Uhr


Benutzeravatar von unwind
entspannen

Das Immer-wunderbar glatt hat g_shell_parse_args() das klingt nach dem, wonach Sie suchen.

Wenn Sie nicht einmal daran interessiert sind, zu zitieren, ist dies möglicherweise übertrieben. Alles, was Sie tun müssen, ist die Tokenisierung, indem Sie Leerzeichen als Tokenzeichen verwenden. Das Schreiben einer einfachen Routine sollte wirklich nicht lange dauern.

Wenn Sie beim Speicher nicht sehr geizig sind, sollte es einfach sein, dies in einem Durchgang ohne Neuzuweisungen zu tun. Nehmen Sie einfach an, dass im schlimmsten Fall jedes zweite Zeichen ein Leerzeichen ist, und nehmen Sie somit eine Zeichenfolge von an n Zeichen enthält höchstens (n + 1) / 2 Argumente, und (natürlich) höchstens n Bytes Argumenttext (ohne Abschlusszeichen).

Benutzeravatar von Joakim
Joakim

Hier ist eine Lösung für Windows und Unix (getestet auf Linux, OSX und Windows). Getestet mit Valgrind und Dr. Speicher.

Es verwendet Wortexp für POSIX-Systeme und CommandLineToArgvW für Windows.

Beachten Sie, dass bei der Windows-Lösung der größte Teil des Codes zwischen konvertiert wird char ** und wchar_t ** mit der schönen Win32-API, da gibt es keine CommandLineToArgvA verfügbar (ANSI-Version).

#ifdef _WIN32
#include <windows.h>
#else
#include <wordexp.h>
#endif

char **split_commandline(const char *cmdline, int *argc)
{
    int i;
    char **argv = NULL;
    assert(argc);

    if (!cmdline)
    {
        return NULL;
    }

    // Posix.
    #ifndef _WIN32
    {
        wordexp_t p;

        // Note! This expands shell variables.
        if (wordexp(cmdline, &p, 0))
        {
            return NULL;
        }

        *argc = p.we_wordc;

        if (!(argv = calloc(*argc, sizeof(char *))))
        {
            goto fail;
        }

        for (i = 0; i < p.we_wordc; i++)
        {
            if (!(argv[i] = strdup(p.we_wordv[i])))
            {
                goto fail;
            }
        }

        wordfree(&p);

        return argv;
    fail:
        wordfree(&p);
    }
    #else // WIN32
    {
        wchar_t **wargs = NULL;
        size_t needed = 0;
        wchar_t *cmdlinew = NULL;
        size_t len = strlen(cmdline) + 1;

        if (!(cmdlinew = calloc(len, sizeof(wchar_t))))
            goto fail;

        if (!MultiByteToWideChar(CP_ACP, 0, cmdline, -1, cmdlinew, len))
            goto fail;

        if (!(wargs = CommandLineToArgvW(cmdlinew, argc)))
            goto fail;

        if (!(argv = calloc(*argc, sizeof(char *))))
            goto fail;

        // Convert from wchar_t * to ANSI char *
        for (i = 0; i < *argc; i++)
        {
            // Get the size needed for the target buffer.
            // CP_ACP = Ansi Codepage.
            needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
                                        NULL, 0, NULL, NULL);

            if (!(argv[i] = malloc(needed)))
                goto fail;

            // Do the conversion.
            needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
                                        argv[i], needed, NULL, NULL);
        }

        if (wargs) LocalFree(wargs);
        if (cmdlinew) free(cmdlinew);
        return argv;

    fail:
        if (wargs) LocalFree(wargs);
        if (cmdlinew) free(cmdlinew);
    }
    #endif // WIN32

    if (argv)
    {
        for (i = 0; i < *argc; i++)
        {
            if (argv[i])
            {
                free(argv[i]);
            }
        }

        free(argv);
    }

    return NULL;
}

Benutzeravatar von Andi
Und ich

Ich habe dies gerade für ein eingebettetes Projekt in einfachem C getan, wo ich eine kleine CLI habe, die die Eingabe der seriellen Schnittstelle analysiert und eine begrenzte Menge von Befehlen mit den Parametern ausführt.

Dies ist wahrscheinlich nicht das Ordentlichste, aber so klein und effizient, wie ich es bekommen könnte:

int makeargs(char *args, int *argc, char ***aa) {
    char *buf = strdup(args);
    int c = 1;
    char *delim;
    char **argv = calloc(c, sizeof (char *));

    argv[0] = buf;

    while (delim = strchr(argv[c - 1], ' ')) {
        argv = realloc(argv, (c + 1) * sizeof (char *));
        argv[c] = delim + 1;
        *delim = 0x00;
        c++;
    }

    *argc = c;
    *aa = argv;

    return c;
}

zu testen:

int main(void) {
    char **myargs;
    int argc;

    int numargs = makeargs("Hello world, this is a test", &argc, &myargs);
    while (numargs) {
        printf("%s\r\n", myargs[argc - numargs--]);
    };

    return (EXIT_SUCCESS);
}

Benutzeravatar von Michael Burr
Michael Burr

Matt Peitreks LIBTINYC hat ein Modul namens argcargv.cpp, das eine Zeichenfolge nimmt und sie unter Berücksichtigung von Argumenten in Anführungszeichen in das Argument-Array ausgibt. Beachten Sie, dass es Windows-spezifisch ist, aber es ist ziemlich einfach, also sollte es einfach sein, auf jede gewünschte Plattform zu wechseln.

  • Mit dem kleinen Problem, dass C++ und nicht C ist 🙂

    – Remo.D

    10. November 2009 um 9:14 Uhr

  • Benennen Sie die Datei in argcargv.c um und es ist C. Buchstäblich.

    – Michael Burr

    10. November 2009 um 15:10 Uhr

  • Die Bibliothek von Herrn Peitrek scheint sehr schwach zu sein, verglichen mit den tatsächlichen Regeln von Microsoft zur Trennung einer Befehlszeile in argc/argv (siehe msdn.microsoft.com/en-us/library/17w5ykft.aspx für ihre Regeln). Natürlich kein Problem, wenn das nicht nötig ist, aber die Leute sollten sicher sein, dass sie bekommen, was sie brauchen!

    – Steve Valliere

    23. Mai 2013 um 15:07 Uhr

  • Außerdem ist es völlig unnötig, da Microsoft Ihnen nicht nur die Spezifikation gibt, wie sie die Befehlszeile analysieren, sondern auch eine API dafür bereitstellt: CommandLineToArgvW

    – Joakim

    28. Februar 2015 um 23:20 Uhr


1393670cookie-checkString in argv/argc parsen

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

Privacy policy