40 Stimmen

String in argv/argc parsen

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

Dies muss nicht auf Windows funktionieren, nur auf Linux - mir ist auch die Anführung von Argumenten egal.

0 Stimmen

Für welche Plattform? Wie Befehlszeilen in argc/argv umgewandelt werden, unterscheidet sich erheblich zwischen Windows und UNIX-basierten Systemen, zum Beispiel. Auf UNIX transformiert die Shell in der Regel die Befehlszeile erheblich, einschließlich des Durchlaufens (Dateimustererweiterung) und der Variablensubstitution. Auf Windows wird die Dateimustererweiterung nicht von der Shell durchgeführt (es sei denn, Sie verwenden etwas wie Cygwin oder das MKS Toolkit).

0 Stimmen

Wenn Sie nicht einmal mit zitierten Argumenten umgehen müssen, würde ich wirklich vorschlagen, Ihre eigene Funktion zu codieren, anstatt eine Bibliothek von Drittanbietern nur für diese Aufgabe einzuführen.

2 Stimmen

Hast du getopt() ausprobiert? (man 3 getopt). Du kannst die Quellen für die meisten Standard-UNIX/Linux-Tools für Beispiele einsehen, eine RIESIGE Anzahl davon. Selbst die Man-Page (zumindest die Linux-Version) enthält ein ordentliches Beispiel. Es gibt auch eine Reihe von Wrapper-Funktionen (du siehst hier Empfehlungen), aber getopt() scheint die einzige verfügbare für JEDE UNIX-Plattform zu sein (tatsächlich scheint sie Teil des POSIX-Standards zu sein).

32voto

Es überrascht mich, dass niemand die einfachste Antwort unter Verwendung der Standard-POSIX-Funktionalität bereitgestellt hat:

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

17voto

sstteevvee Punkte 309

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

  • Die Verwendung von strtok ändert den ursprünglichen "commandLine"-String ab und ersetzt die Leerzeichen durch \0-End-of-String-Trennzeichen
  • argv[] zeigt auf "commandLine", also ändere es nicht, bis du mit argv[] fertig bist.

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;

Du kannst jetzt argc und argv verwenden oder sie an andere Funktionen übergeben, die wie "foo(int argc, char **argv)" deklariert sind.

16voto

Remo.D Punkte 15552

Wenn die einfachste Lösung für Ihren Fall zu aufwändig ist, könnten Sie in Betracht ziehen, eine selbst zu programmieren.

Dann können Sie:

  • den String scannen und zählen, wie viele Argumente es gibt (und Sie erhalten Ihr argc)
  • ein Array von char * (für Ihr argv) zuweisen
  • den String erneut durchsuchen, die Zeiger im zugewiesenen Array zuweisen und Leerzeichen durch '\0' ersetzen (wenn Sie den String, der die Argumente enthält, nicht ändern können, sollten Sie ihn duplizieren).
  • vergessen Sie nicht, das Allokierte freizugeben!

Das folgende Diagramm sollte es hoffentlich verdeutlichen:

             aa bbb ccc "dd d" ee         <- Original-String

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

Ein möglicher API könnte sein:

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

Sie benötigen zusätzliche Überlegungen, um freeparsedargs() sicher zu implementieren.

Wenn Ihr String sehr lang ist und Sie nicht zweimal scannen möchten, könnten Sie Alternativen in Betracht ziehen, wie z. B. das Allozieren von mehr Elementen für die argv-Arrays (und bei Bedarf eine Neuallokation).

EDIT: Vorgeschlagene Lösung (behandelt kein argument in Anführungszeichen).

    #include 

    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);
    }

11voto

unwind Punkte 377331

Das immerwunderbare glib hat [g_shell_parse_args()](https://docs.gtk.org/glib/func.shell_parse_argv.html), das klingt nach dem, was du suchst.

Wenn du nicht einmal Angebotszeichen verwenden möchtest, ist das vielleicht zu viel des Guten. Alles was du tun musst, ist Tokenisierung, wobei Leerzeichen als Tokenzeichen verwendet werden. Eine einfache Routine dafür zu schreiben, sollte wirklich nicht lange dauern.

Wenn du nicht allzu pingelig beim Speicher bist, ist es einfach, es in einem Durchlauf ohne Neuzuweisungen zu machen; geh einfach vom schlimmsten Fall aus, dass jedes zweite Zeichen ein Leerzeichen ist, und nehmen an, dass eine Zeichenfolge mit n Zeichen höchstens (n + 1) / 2 Argumente enthält, und (natürlich) höchstens n Byte an Argumenttext (ohne Terminatoren).

7voto

Joakim Punkte 10478

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

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

Zu beachten ist, dass für die Windows-Lösung der Großteil des Codes zwischen char ** und wchar_t ** mit der schönen Win32-API konvertiert wird, da keine CommandLineToArgvA (ANSI-Version) verfügbar ist.

#ifdef _WIN32
#include 
#else
#include 
#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;

        // Beachten! Dies erweitert Shell-Variablen.
        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;

        // Konvertierung von wchar_t * zu ANSI char *
        for (i = 0; i < *argc; i++)
        {
            // Größe des Ziel-Puffers abrufen.
            // CP_ACP = Ansi-Codepage.
            needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
                                        NULL, 0, NULL, NULL);

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

            // Die Umwandlung durchführen.
            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;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X