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).

4voto

Andi Punkte 981

Ich habe das gerade für ein eingebettetes Projekt in reinem C gemacht, bei dem ich eine kleine CLI habe, die die serielle Porteingabe analysiert und eine begrenzte Anzahl von Befehlen mit Parametern ausführt.

Das ist wahrscheinlich nicht das Ordentlichste, aber so klein und effizient wie möglich:

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

zum Testen:

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

    int numargs = makeargs("Hallo Welt, das ist ein Test", &argc, &myargs);
    while (numargs) {
        printf("%s\r\n", myargs[argc - numargs--]);
    };

    return (EXIT_SUCCESS);
}

2voto

Michael Burr Punkte 320591

Matt Peitrek's LIBTINYC hat ein Modul namens argcargv.cpp, das einen String nimmt und ihn in das Argument-Array analysiert, wobei auch argumentierte Argumente berücksichtigt werden. Beachten Sie, dass es Windows-spezifisch ist, aber ziemlich einfach ist und daher leicht auf die Plattform Ihrer Wahl übertragen werden kann.

2voto

codebox Punkte 19101

Ich habe schließlich eine Funktion geschrieben, um dies selbst zu tun. Ich denke nicht, dass es sehr gut ist, aber es funktioniert für meinen Zweck - gerne Verbesserungen vorschlagen für alle anderen, die dies in Zukunft benötigen:

void parseCommandLine(char* cmdLineTxt, char*** argv, int* argc){
    int count = 1;

    char *cmdLineCopy = strdupa(cmdLineTxt);
    char* match = strtok(cmdLineCopy, " ");
 // Zuerst die Anzahl der Argumente zählen
    while(match != NULL){
        count++;
        match = strtok(NULL, " ");
    }

    *argv = malloc(sizeof(char*) * (count+1));
    (*argv)[count] = 0;
    **argv = strdup("test"); // Der Programmname würde normalerweise hier stehen

    if (count > 1){
        int i=1;
        cmdLineCopy = strdupa(cmdLineTxt);
        match = strtok(cmdLineCopy, " ");
        do{
            (*argv)[i++] = strdup(match);
            match = strtok(NULL, " ");
        } while(match != NULL);
     }

    *argc = count;
}

1voto

kyb Punkte 5908

Betrachten Sie eine weitere Implementierung. Ausführen.

#include   //   für isspace()

/** 
 * Trennen Sie das nächste nicht-leere Wort aus einem String heraus.
 * @note Kein nullptr-Schutz
 * @param str  [IN]   Zeiger auf Zeiger auf den String. Verschachtelter Zeiger auf String wird geändert.
 * @param word [OUT]  Zeiger auf den Zeiger des nächsten Wortes. Zum Ausfüllen.
 * @return  Zeiger auf Zeichenfolge - aktueller Cursor. Überprüfen Sie darauf, ob '\0' aufgerufen werden soll anhalten diese Funktion  
 */
static char* splitArgv(char **str, char **word)
{
    constexpr char QUOTE = '\'';
    bool inquotes = false;

    // Optimierung
    if( **str == 0 )
        return NULL;

    // Führende Leerzeichen überspringen.
    while (**str && isspace(**str)) 
        (*str)++;

    if( **str == '\0')
        return NULL;

    // Satz in Anführungszeichen ist ein Argument
    if( **str == QUOTE ){
        (*str)++;
        inquotes = true;
    }

    // Setzen Sie den Satzanfang
    *word = *str;

    // Alle Zeichen überspringen, wenn in Anführungszeichen
    if( inquotes ){
        while( **str && **str!=QUOTE )
            (*str)++;
        //if( **str!= QUOTE )
    }else{
        // Überspringen von Nicht-Leerzeichenzeichen.
        while( **str && !isspace(**str) )
            (*str)++;
    }
    // Satz nullterminieren und den `str`-Zeiger auf das nächste Symbol setzen
    if(**str)
        *(*str)++ = '\0';

    return *str;
}

/// Um die Standardkonvention zu unterstützen wird `argv[argc]` auf `NULL` gesetzt
///\param[IN]  str : Eingabestring. Wird geändert - in Teilzeichenfolgen aufgeteilt
///\param[IN]  argc_MAX : Maximales a rgc, mit anderen Worten Größe des Eingabearrays \p argv
///\param[OUT] argc : Anzahl der auszufüllenden Argumente
///\param[OUT] argv : Array von Zeigern auf C-Zeichenfolgen, die ausgefüllt werden sollen. All diese Zeichenfolgen sind Teilzeichenfolgen von \p str
///\return Zeiger auf den Rest des Strings. Überprüfen Sie, ob er '\0' und wissen Sie, ob noch etwas analysiert werden soll. \
///        Wenn das Ergebnis !='\0' ist, ist \p argc_MAX zu klein, um alles zu analysieren. 
char* parseStrToArgcArgvInsitu( char *str, const int argc_MAX, int *argc, char* argv[] )
{
    *argc = 0;
    while( *argc

``### Nutzungscode

#include 
using namespace std;

void parseAndPrintOneString(char *input)
{
    constexpr size_t argc_MAX = 5;
    char* v[argc_MAX] = {0};
    int c=0;

    char* rest = parseStrToArgcArgvInsitu(input,argc_MAX,&c,v);
    if( *rest!='\0' )  // oder klarer `strlen(rest)==0` aber nicht effizient
        cout<<"Es gibt noch etwas zu analysieren. argc_MAX ist zu klein."<

`### Ausgabe:

Zeile analysieren "Just another TEST\r\n":
argc : 3
argv[0] : Just
argv[1] : another
argv[2] : TEST

Zeile analysieren "  Hello my world 'in quotes'   !":
Es gibt noch etwas zu analysieren. argc_MAX ist zu klein.
argc : 4
argv[0] : Hello
argv[1] : my
argv[2] : world
argv[3] : in quotes

Zeile analysieren "./hi 'Less is more'":
argc : 2
argv[0] : ./hi
argv[1] : Less is more

Zeile analysieren "Very long line with "double quotes" should be parsed several times if argv[] buffer is small":
Es gibt noch etwas zu analysieren. argc_MAX ist zu klein.
argc : 4
argv[0] : Very
argv[1] : long
argv[2] : line
argv[3] : with

Zeile analysieren "       

":
argc : 0` ``

1voto

Zibri Punkte 7918

Dieses habe ich auch geschrieben und berücksichtigt auch Anführungszeichen (aber nicht verschachtelt)

Fühlen Sie sich frei, beizutragen.

/*
String-Tokenisierung unter Berücksichtigung von Anführungszeichen.
Von Zibri 
https://github.com/Zibri/tokenize
*/

#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
  char *str1, *token;
  int j;
  char *qstart = NULL;
  bool quoted = false;

  if (argc != 2) {
    fprintf(stderr, "Verwendung: %s Zeichenkette\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  for (j = 1, str1 = argv[1];; j++, str1 = NULL) {
    token = strtok(str1, " ");
    if (token == NULL)
      break;
    if ((token[0] == 0x27) || (token[0] == 0x22)) {
      qstart = token + 1;
      quoted = true;
    }
    if ((token[strlen(token) - 1] == 0x27) || (token[strlen(token) - 1] == 0x22)) {
      quoted = false;
      token[strlen(token) - 1] = 0;
      printf("%d: %s\n", j, qstart);
    } else {
      if (quoted) {
        token[strlen(token)] = 0x20;
        j--;
      } else
        printf("%d: %s\n", j, token);
    }
  }

  if (quoted) {
    fprintf(stderr, "Fehler bei der Zeichenfolgenangabe\n");
    return EXIT_FAILURE;
  } else
    return EXIT_SUCCESS;
}

Beispielausgabe:

$ ./tokenize "1 2 3 '4 5 6' 7 8 \"test abc\" 10 11"
1: 1
2: 2
3: 3
4: 4 5 6
5: 7
6: 8
7: test abc
8: 10
9: 11

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