Analizzare la stringa argv/argc

C’è un modo in C per analizzare un pezzo di testo e ottenere i valori di argv e argc, come se il testo era stato passato a un’applicazione riga di comando?

Questo non funziona su Windows, Linux, anche io non cura sulla citazione di argomenti.

  • Per quale piattaforma? Come righe di comando ottenere analizzato in argc/argv è molto diversa tra Windows e i sistemi basati su UNIX, per esempio. Su UNIX, shell in genere trasforma la riga di comando in modo significativo, anche la funzione glob (file modello di espansione), così come la variabile substituion. Su Windows il file di modello di espansione non è fatto con la shell (a meno che non stai usando qualcosa come cygwin o MKS Toolkit, naturalmente).
  • Se non hai nemmeno bisogno di gestire citato args, mi sento di suggerire di codifica propria funzione, piuttosto che introdurre un 3rd party libreria solo per questo compito.
  • Hai provato getopt()? (3 uomo getopt). Si può vedere la maggior parte dei sistemi UNIX/Linux standard strumenti di fonti per esempi, in un numero ENORME di loro. Anche la pagina man (almeno per Linux) contiene un esempio decente. C’è anche il numero di wrapper (vedi consigli) ma getopt() sembra essere l’unico disponibile per QUALSIASI piattaforma UNIX (in realtà sembra essere parte di standard POSIX).
  • Se ur ancora interessato e vuole forza industriale da zero, in un piccolo pacchetto di codice. Cerca in questa pagina per nargv Da lontano soluzione migliore che ho visto qui, in puro c codice. Si prega di Votare questa Risposta Fino! Così altri possono trovare.
  • Ho fatto ricerca per nargv e il tuo commento è l’unico colpo. Così ho cercato su google: github.com/hypersoft/nargv … Alcuni commenti però. Questo utilizza C99, in questo modo non funziona il compilatore Microsoft C. Anche l’idea è di avere unit test con un sacco di casi di test che consente di verificare ogni tipo di scenario per il parser per assicurarsi che funzioni come previsto.
  • Per unix sviluppatori che vogliono un implementazione semplice ma potente che gestisce bash in stile escape e per la citazione: github.com/pasztorpisti/cmd2argv
  • Probabilmente anche la voglia di glob, in modo da vedere glob(7) e seguire le referenze
  • Non so perché getopt è sempre upvotes qui — risolve un problema diverso (analizzare il contenuto di un array argv, piuttosto che la costruzione di un array argv da una stringa, che è quello che l’OP ha chiesto).
  • check this out: stackoverflow.com/a/54617539/236062

InformationsquelleAutor codebox | 2009-11-10

 

12 Replies
  1. 11

    Se glib soluzione è eccessivo per il vostro caso, si può considerare di codifica uno voi stessi.

    Poi si può:

    • la scansione della stringa e contare quanti argomenti ci sono (e si ottiene la argc)
    • allocare un array di char * (per le argv)
    • ripetere la scansione della stringa, assegnare i puntatori array allocato e sostituire gli spazi con ‘\0’ (se non è possibile modificare la stringa contenente gli argomenti, si dovrebbe duplicare).
    • non dimenticate di liberare ciò che è stato allocato!

    Il diagramma qui sotto dovrebbe chiarire (si spera):

                 aa bbb ccc "dd d" ee         <- original string
    
                 aa0bbb0ccc00dd d00ee0        <- transformed string
                 |  |   |    |     |
       argv[0] __/  /   /    /     /
       argv[1] ____/   /    /     /
       argv[2] _______/    /     /
       argv[3] ___________/     /
       argv[4] ________________/ 

    Un possibile API potrebbe essere:

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

    Avrete bisogno di ulteriori considerazioni per implementare freeparsedargs() in modo sicuro.

    Se la stringa è molto lunga e non si desidera eseguire la scansione due volte si può prendere in considerazione alteranatives come l’allocazione di ulteriori elementi di array argv (e riallocazione se necessario).

    EDIT: la soluzione Proposta (desn non in grado di gestire citato argomento).

        #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);
        }
    • perché getopt fa un lavoro diverso. Si prende un array di argomenti e cercare le opzioni in esso. Questa domanda è la divisione di una stringa di “argomenti” in un array di char *, che è qualcosa che getopt non è in grado di fare
    • Se si trasforma la stringa in input come che non si può fare di concatenazione di stringhe tra virgolette” come “questo” o ” questo. Vedi la mia risposta per una completa soluzione.
    • (nit-picking avanti) Nota che c’è una piccola cosa manca per essere compatibile con lo standard argc/argv layout: L’ingresso dietro l’ultimo valido in argv è sempre impostato a NULL ("foo bar": argv[0] -> "foo", argv[1] -> "bar", argv[2] -> NULL).
  2. 29

    Sono sorpreso che nessuno ha fornito la risposta più semplice l’utilizzo di standard POSIX funzionalità:

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

    • Che può fare di più di quanto si desidera. E. g. non shell parola espansioni tra cui la variabile di ambiente di sostituzione, ad esempio sostituendo $PATH con il percorso corrente.
    • Credo dipenda da cosa si intende per analizzare in argc/argv; certo che coinvolge ciò che fa la shell (elaborazione citando), ma l’espansione di una variabile e le altre cose sono più discutibili. BTW wordexp ha un’opzione per disattivare il comando di espansione.
    • Se intendi WRDE_NOCMD, che non sembra impedire l’espansione di $PATH, né l’espansione * per i nomi dei file nella directory corrente.
    • Non ho detto che ha impedito l’espansione di una variabile, solo che un’altra cosa che si desidera disattivare, espansione di comando, può essere disattivata.
    • Questo è esattamente quello che stavo cercando e sembra funzionare molto bene. Ho bisogno di passare un comando definito dall’utente per posix_spawn, non sapendo se ci sarebbero altri argomenti. Tuttavia, un breve esempio di codice sarebbe questa risposta, tanto meglio. Sì, anche ora, più di sette anni più tardi. 🙂
  3. 15

    Ecco il mio contributo. La sua bella e breve, ma le cose di cui diffidare sono:

    • Utilizzo di strtok modifica l’originale “riga di comando” string, sostituendo gli spazi con \0 di fine stringa di delimitatori
    • argv[] finisce che punta in “riga di comando”, quindi non modificarlo fino a quando non hai finito con argv[].

    Codice:

    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;

    È ora possibile utilizzare argc e argv, o passare ad altre funzioni dichiarate come “foo(int argc, char **argv)”.

    • Grazie, salvato qualche tempo. Per tutti coloro che utilizzano questo: “char* p1” (anche se il compilatore avrebbe detto =] )
    • E l’utilizzo di strtok non è threadsafe..
    • questo considera anche le virgolette stackoverflow.com/a/54617539/236062
  4. 9

    Sempre meraviglioso glib ha g_shell_parse_args() che suona come quello che si sta dopo.

    Se non sei interessato anche il quoting, questo potrebbe essere eccessivo. Tutto quello che dovete fare è dividere, utilizzando gli spazi come un segno di carattere. La scrittura di una semplice routine di fare che non dovrebbe durare a lungo, davvero.

    Se non siete super-avaro di memoria, di farlo in un unico passaggio, senza la riassegnazione dovrebbe essere facile; basta assumere un caso peggiore di ogni secondo carattere di essere uno spazio, assumendo quindi una stringa di n caratteri contiene al massimo (n + 1) /2 argomenti, e (ovviamente) in più n byte di testo di argomento (esclusi i terminatori).

  5. 7

    Ecco una soluzione per Windows e Unix (testato su Linux, OSX e Windows). Testato con Valgrind e Dr. Memoria.

    Utilizza wordexp per sistemi POSIX, e CommandLineToArgvW per Windows.

    Nota che per la soluzione Windows, la maggior parte del codice è la conversione tra i char ** e wchar_t ** con la bella API Win32, dal momento che non c’è CommandLineToArgvA disponibile (ANSI-versione).

    #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;
    }
  6. 3

    Ho appena fatto questo per un integrato di progetto in C, dove ho un po ‘ di CLI, che analizza il seriale porta di ingresso ed esegue un set limitato di comandi con i parametri.

    Questo probabilmente non è il più grazioso, ma come piccolo ed efficiente, come ho potuto ottenere:

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

    di test:

    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);
    }
    • Per essere un po ‘ più vicino allo standard argv, aggiungere un extra di posizione alla fine con NULL. Questo viene fatto in caso di un programmatore ignora argc e solo while(process(*++argv)); fino a che hanno colpito quel NULL. Ci sarebbe, naturalmente, ha bisogno di essere più a gestire citato argomenti (ed è scappato virgolette).
  7. 2

    Matt Peitrek del LIBTINYC dispone di un modulo chiamato argcargv.cpp che prende una stringa e analizza fuori l’argomento array di prendere citato argomenti in considerazione. Nota che è specifico per Windows, ma è abbastanza semplice, quindi dovrebbe essere facile da spostare qualunque sia la piattaforma che si desidera.

    • Con il piccolo problema che è il C++ e non C 🙂
    • Rinominare il file argcargv.c e C. Letteralmente.
    • Signor Peitrek biblioteca sembra essere molto deboli rispetto a Microsoft attuali regole per la separazione di una riga di comando in argc/argv (vedi msdn.microsoft.com/en-us/library/17w5ykft.aspx per le loro regole). Egli non sembra maniglia incorporata stringhe tra virgolette, più barre rovesciate o anche di escape dei caratteri di citazione. Non è un problema se non è necessario, naturalmente, ma la gente dovrebbe essere sicuri di ottenere ciò di cui hanno bisogno!
    • Inoltre, è totalmente inutile dal momento che Microsoft non solo dare la specifica di come si analizza la riga di comando, ma anche di fornire un’API per questo: CommandLineToArgvW
  8. 2

    Ho finito per scrivere una funzione per fare questo a me stesso, non credo che la sua molto buona, ma funziona per i miei scopi – sentitevi liberi di suggerire miglioramenti per chiunque altro che ha bisogno di questo in futuro:

    void parseCommandLine(char* cmdLineTxt, char*** argv, int* argc){
        int count = 1;
    
        char *cmdLineCopy = strdupa(cmdLineTxt);
        char* match = strtok(cmdLineCopy, " ");
     //First, count the number of arguments
        while(match != NULL){
            count++;
            match = strtok(NULL, " ");
        }
    
        *argv = malloc(sizeof(char*) * (count+1));
        (*argv)[count] = 0;
        **argv = strdup("test"); //The program name would normally go in here
    
        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;
    }
    • Mi piace la brevità della tua soluzione, ma io non sono un grande fan di strtok() o strdupa(). Io non sono molto chiari su ciò che il strdup(“test”) per. Lo svantaggio principale mi sembra il fatto che hai molte strdup e, di conseguenza, si avrà a che fare, molti free() quando fatto. Ho postato una versione alternativa nella mia risposta, nel caso in cui potrebbe essere utile per qualcuno.
    • So che è molto tempo fa, ma stavo lavorando su questo stesso problema generale me e per usare strtok. Sembra progettato per solo un caso. Quindi, sono curioso: Perché “non sono un grande fan di strtok()“?
    • 1: modifica il buffer si analizza, 2: ricorda buffer attraverso chiamate, il che rende 3: non è thread-safe, come non è rientrante. Non è progettato scopo di strtok ma progettato in effetti collaterali che sono fastidiosi. 🙂 🙂 🙂
  9. 1

    In considerazione un’altra applicazione. Eseguire.

    #include <cctype>  //<ctype.h>  for isspace()
    
    /** 
     * Parse out the next non-space word from a string.
     * @note No nullptr protection
     * @param str  [IN]   Pointer to pointer to the string. Nested pointer to string will be changed.
     * @param word [OUT]  Pointer to pointer of next word. To be filled.
     * @return  pointer to string - current cursor. Check it for '\0' to stop calling this function   
     */
    static char* splitArgv(char **str, char **word)
    {
        constexpr char QUOTE = '\'';
        bool inquotes = false;
    
        //optimization
        if( **str == 0 )
            return NULL;
    
        //Skip leading spaces.
        while (**str && isspace(**str)) 
            (*str)++;
    
        if( **str == '\0')
            return NULL;
    
        //Phrase in quotes is one arg
        if( **str == QUOTE ){
            (*str)++;
            inquotes = true;
        }
    
        //Set phrase begining
        *word = *str;
    
        //Skip all chars if in quotes
        if( inquotes ){
            while( **str && **str!=QUOTE )
                (*str)++;
            //if( **str!= QUOTE )
        }else{
            //Skip non-space characters.
            while( **str && !isspace(**str) )
                (*str)++;
        }
        //Null terminate the phrase and set `str` pointer to next symbol
        if(**str)
            *(*str)++ = '\0';
    
        return *str;
    }
    
    
    ///To support standart convetion last `argv[argc]` will be set to `NULL`
    ///\param[IN]  str : Input string. Will be changed - splitted to substrings
    ///\param[IN]  argc_MAX : Maximum a rgc, in other words size of input array \p argv
    ///\param[OUT] argc : Number of arguments to be filled
    ///\param[OUT] argv : Array of c-string pointers to be filled. All of these strings are substrings of \p str
    ///\return Pointer to the rest of string. Check if for '\0' and know if there is still something to parse. \
    ///       If result !='\0' then \p argc_MAX is too small to parse all. 
    char* parseStrToArgcArgvInsitu( char *str, const int argc_MAX, int *argc, char* argv[] )
    {
        *argc = 0;
        while( *argc<argc_MAX-1  &&  splitArgv(&str, &argv[*argc]) ){
            ++(*argc);
            if( *str == '\0' )
                break;
        }
        argv[*argc] = nullptr;
        return str;
    };

    Utilizzo codice

    #include <iostream>
    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' )  //or more clear `strlen(rest)==0` but not efficient
            cout<<"There is still something to parse. argc_MAX is too small."<<endl;
    
        cout << "argc : "<< c << endl;
        for( int i=0; i<c; i++ )
            cout<<"argv["<<i<<"] : "<<v[i] <<endl;
        /*//or condition is `v[i]`
        for( int i=0; v[i]; i++ )
            cout<<"argv["<<i<<"] : "<<v[i] <<endl;*/
    }
    
    
    
    int main(int argc, char* argv[])
    {
        char inputs[][500] ={
                  "Just another TEST\r\n"
                , "  Hello my world 'in quotes' \t !"
                , "./hi 'Less is more'"
                , "Very long line with \"double quotes\" should be parsed several times if argv[] buffer is small"
                , "   \t\f \r\n"
        };
    
        for( int i=0; i<5; ++i ){
            cout<<"Parsing line \""<<inputs[i]<<"\":"<<endl;
            parseAndPrintOneString(inputs[i]);
            cout<<endl;
        }
    }

    Di uscita:

    Parsing line "Just another TEST\r\n":
    argc : 3
    argv[0] : Just
    argv[1] : another
    argv[2] : TEST
    
    Parsing line "  Hello my world 'in quotes'   !":
    There is still something to parse. argc_MAX is too small.
    argc : 4
    argv[0] : Hello
    argv[1] : my
    argv[2] : world
    argv[3] : in quotes
    
    Parsing line "./hi 'Less is more'":
    argc : 2
    argv[0] : ./hi
    argv[1] : Less is more
    
    Parsing line "Very long line with "double quotes" should be parsed several times if argv[] buffer is small":
    There is still something to parse. argc_MAX is too small.
    argc : 4
    argv[0] : Very
    argv[1] : long
    argv[2] : line
    argv[3] : with
    
    Parsing line "       
    
    ":
    argc : 0
  10. 1

    Soluzione per coloro che non vogliono utilizzare l’allocazione dinamica della memoria (E. g. embedded)

    Ho scritto tokenise_to_argc_argv() per un integrato di progetto, che utilizza strtok_r() come base per tokenising una stringa di comando in argc e argv forma. A differenza della maggior parte delle risposte qui, io di solito allocare la memoria in modo statico. Quindi, la mia attuazione presuppone che si dispone di un limite superiore di argv_length. Per la maggior parte delle applicazioni embedded, questo è più che sufficiente. Ho incluso un codice di esempio qui di seguito in modo da poter rapidamente usarlo.

    int tokenise_to_argc_argv(
            char     *buffer,     ///< In/Out : Modifiable String Buffer To Tokenise
            int      *argc,       ///< Out    : Argument Count
            char     *argv[],     ///< Out    : Argument String Vector Array
            const int argv_length ///< In     : Maximum Count For `*argv[]`
          )
    { /* Tokenise string buffer into argc and argv format (req: string.h) */
      int i = 0;
      for (i = 0 ; i < argv_length ; i++)
      { /* Fill argv via strtok_r() */
        if ( NULL == (argv[i] = strtok_r( NULL , " ", &buffer)) ) break;
      }
      *argc = i;
      return i; //Argument Count
    }

    Nota:

    • Fornito carattere del buffer deve essere modificabile (come strtok_r() inserisce \0 nel buffer per delimitare la stringa di token).
    • strtok_r in questa funzione è attualmente in uso " " carattere di spazio il solo deliminator. Questo emula il comportamento main(int argc, char *argv[]) in tipico interfacce a riga di comando.
    • Questa funzione non fa uso di malloc o calloc, invece, sarà necessario allocare l’array argv separatamente, e di fornire la lunghezza di argv in modo esplicito. Questo perché ho intenzione di usare questo per dispositivi embedded e quindi piuttosto assegnare manualmente.
    • strtok_r() è utilizzato perché è threadsafe (Dal strtok() utilizza un interno statico puntatore). Inoltre fa parte della libreria C standard string.h così è molto portabile.

    Di seguito, sono la dimostrazione di codice così come è uscita. Inoltre, questo dimostra che tokenise_to_argc_argv() in grado di gestire la maggior parte di stringa casi e così è stato testato. Anche questa funzione non si basa su malloc o calloc e quindi è adatto per applicazioni embedded di utilizzo (dopo l’uso di stdint.h tipi).


    Dimostrazione Codice

    /*******************************************************************************
      Tokenise String Buffer To Argc and Argv Style Format
      Brian Khuu 2017
    *******************************************************************************/
    #include <stdio.h>  //printf()
    #include <ctype.h>  //isprint()
    #include <string.h> //strtok_r()
    
    /**-----------------------------------------------------------------------------
      @brief Tokenise a string buffer into argc and argv format
    
      Tokenise string buffer to argc and argv form via strtok_r()
      Warning: Using strtok_r will modify the string buffer
    
      Returns: Number of tokens extracted
    
    ------------------------------------------------------------------------------*/
    int tokenise_to_argc_argv(
            char     *buffer,     ///< In/Out : Modifiable String Buffer To Tokenise
            int      *argc,       ///< Out    : Argument Count
            char     *argv[],     ///< Out    : Argument String Vector Array
            const int argv_length ///< In     : Maximum Count For `*argv[]`
          )
    { /* Tokenise string buffer into argc and argv format (req: string.h) */
      int i = 0;
      for (i = 0 ; i < argv_length ; i++)
      { /* Fill argv via strtok_r() */
        if ( NULL == (argv[i] = strtok_r( NULL, " ", &buffer)) ) break;
      }
      *argc = i;
      return i; //Argument Count
    }
    
    /*******************************************************************************
      Demonstration of tokenise_to_argc_argv()
    *******************************************************************************/
    
    static void print_buffer(char *buffer, int size);
    static void print_argc_argv(int argc, char *argv[]);
    static void demonstrate_tokenise_to_argc_argv(char buffer[], int buffer_size);
    
    int main(void)
    { /* This shows various string examples */
      printf("# `tokenise_to_argc_argv()` Examples\n");
      { printf("## Case0: Normal\n");
        char  buffer[] = "tokenising example";
        demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
      }
      { printf("## Case1: Empty String\n");
        char  buffer[] = "";
        demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
      }
      { printf("## Case2: Extra Space\n");
        char  buffer[] = "extra  space here";
        demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
      }
      { printf("## Case3: One Word String\n");
        char  buffer[] = "one-word";
        demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
      }
    }
    
    static void demonstrate_tokenise_to_argc_argv(char buffer[], int buffer_size)
    { /* This demonstrates usage of tokenise_to_argc_argv */
      int   argc     = 0;
      char *argv[10] = {0};
    
      printf("* **Initial State**\n");
      print_buffer(buffer, buffer_size);
    
      /* Tokenise Command Buffer */
      tokenise_to_argc_argv(buffer, &argc, argv, sizeof(argv));
    
      printf("* **After Tokenizing**\n");
      print_buffer(buffer, buffer_size);
      print_argc_argv(argc,argv);
      printf("\n\n");
    }
    
    static void print_buffer(char *buffer, int size)
    {
      printf(" - Buffer Content `");
      for (int i = 0 ; i < size; i++) printf("%c",isprint(buffer[i])?buffer[i]:'0');
      printf("` | HEX: ");
      for (int i = 0 ; i < size; i++) printf("%02X ", buffer[i]);
      printf("\n");
    }
    
    static void print_argc_argv(int argc, char *argv[])
    { /* This displays the content of argc and argv */
      printf("* **Argv content** (argc = %d): %s\n", argc, argc ? "":"Argv Is Empty");
      for (int i = 0 ; i < argc ; i++) printf(" - `argv[%d]` = `%s`\n", i, argv[i]);
    }

    Uscita

    tokenise_to_argc_argv() Esempi

    Case0: Normale

    • Stato Iniziale
      • Il Contenuto del Buffer tokenising example0 | HEX: 74 6F 6B 65 6E 69 73 69 6E 67 20 65 78 61 6D 70 6C 65 00
    • Dopo La Creazione Di Token
      • Il Contenuto del Buffer tokenising0example0 | HEX: 74 6F 6B 65 6E 69 73 69 6E 67 00 65 78 61 6D 70 6C 65 00
    • Argv contenuto (argc = 2):
      • argv[0] = tokenising
      • argv[1] = example

    Caso 1: Stringa Vuota

    • Stato Iniziale
      • Il Contenuto del Buffer 0 | HEX: 00
    • Dopo La Creazione Di Token
      • Il Contenuto del Buffer 0 | HEX: 00
    • Argv contenuto (argc = 0): Argv È Vuoto

    Caso 2: Spazio Extra

    • Stato Iniziale
      • Il Contenuto del Buffer extra space here0 | HEX: 65 78 74 72 61 20 20 73 70 61 63 65 20 68 65 72 65 00
    • Dopo La Creazione Di Token
      • Il Contenuto del Buffer extra0 space0here0 | HEX: 65 78 74 72 61 00 20 73 70 61 63 65 00 68 65 72 65 00
    • Argv contenuto (argc = 3):
      • argv[0] = extra
      • argv[1] = space
      • argv[2] = here

    Case3: Una Parola Stringa

    • Stato Iniziale
      • Il Contenuto del Buffer one-word0 | HEX: 6F 6E 65 2D 77 6F 72 64 00
    • Dopo La Creazione Di Token
      • Il Contenuto del Buffer one-word0 | HEX: 6F 6E 65 2D 77 6F 72 64 00
    • Argv contenuto (argc = 1):
      • argv[0] = one-word

    Stringa mancante.h o strtok_r() nella toolchain in qualche modo?

    Se per qualche motivo la toolchain non hanno strtok_r(). È possibile utilizzare questa versione semplificata di strtok_r().
    Si tratta di una versione modificata della GNU C attuazione del strtok_r(), ma semplificato per supportare solo il carattere di spazio.

    Di utilizzare questo, basta posizionarlo sulla parte superiore del tokenise_to_argc_argv() quindi sostituire strtok_r( NULL, " ", &buffer)
    con strtok_space(&buffer)

    /**-----------------------------------------------------------------------------
      @brief Simplied space deliminated only version of strtok_r()
    
      - save_ptr : In/Out pointer to a string. This pointer is incremented by this
                    function to find and mark the token boundry via a `\0` marker.
                    It is also used by this function to find mutiple other tokens
                    via repeated calls.
    
      Returns:
        - NULL  : No token found
        - pointer to start of a discovered token
    
    ------------------------------------------------------------------------------*/
    char * strtok_space(char **save_ptr)
    { /* strtok_space is slightly modified from GNU C Library `strtok_r()`  implementation. 
          Thus this function is also licenced as GNU Lesser General Public License*/
      char *start = *save_ptr;
      char *end = 0;
    
      if (*start == '\0') {
        *save_ptr = start;
        return NULL;
      }
    
      /* Scan leading delimiters.  */
      while(*start == ' ') start++;
      if (*start == '\0') {
        *save_ptr = start;
        return NULL;
      }
    
      /* Find the end of the token.  */
      end = start;
      while((*end != '\0') && (*end != ' ')) end++;
      if (*end == '\0') {
        *save_ptr = end;
        return start;
      }
    
      /* Terminate the token and make *SAVE_PTR point past it.  */
      *end = '\0';
      *save_ptr = end + 1;
      return start;
    }
    • bello, ma non cita né virgolette
  11. 1

    Questo ho scritto, inoltre, ritiene citazioni (ma non nidificati)

    Sentitevi liberi di contribuire.

    /*
    Tokenize string considering also quotes.
    By Zibri <zibri AT zibri DOT org>
    https://github.com/Zibri/tokenize
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    
    int main(int argc, char *argv[])
    {
      char *str1, *token;
      int j;
      char *qstart = NULL;
      bool quoted = false;
    
      if (argc != 2) {
        fprintf(stderr, "Usage: %s string\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, "String quoting error\n");
        return EXIT_FAILURE;
      } else
        return EXIT_SUCCESS;
    }

    Esempio di output:

    $ ./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
    • Ovviamente può essere modificato per includere anche le staffe, come “citazioni”…
    • Nota: si distrugge la stringa di origine (perché utilizza strtok) di tenere a mente e, se necessario, aggiungere un strdup
  12. 0

    Purtroppo C++, ma per gli altri che potrebbero cercare questo tipo di libreria mi raccomando:

    ParamContainer – facile-per-uso della riga di comando parametro parser

    Davvero piccolo e molto semplice.

    p.addParam("long-name", 'n', ParamContainer::regular, 
               "parameter description", "default_value");  

    nomeprogramma-lungo-nome=valore

    cout << p["long-name"];
    >> value

    Dalla mia esperienza:

    • molto utile e semplice
    • stabile di produzione
    • ben testato (da me)
    • Questo è interessante: seconda risposta, suggerendo un C++ soluzione per un C domanda …
    • Hai ragione, ho post, perché quando stavo guardando I sorgenti di qualche tempo fa, mi ricordo che era generico, INONDAZIONE, codice libero, che sembrava quasi C., Ma penso che sia la pena di continuare questo qui.

Lascia un commento