read from fortnite c arguments

from - Analizar cadena en argv/argc



read from command line c (11)

Considere otra implementación más. Run

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

Codigo de uso

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

Salida:

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

¿Hay alguna forma en C de analizar un fragmento de texto y obtener valores para argv y argc, como si el texto se hubiera pasado a una aplicación en la línea de comandos?

Esto no tiene que funcionar en Windows, solo en Linux. Tampoco me importa citar argumentos.


Acabo de hacer esto para un proyecto integrado en C simple, donde tengo una pequeña CLI que analiza la entrada del puerto serie y ejecuta un conjunto limitado de comandos con los parámetros.

Probablemente este no sea el más limpio, pero tan pequeño y eficiente como podría conseguirlo:

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

Probar:

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


Aquí está mi contribución. Es bonito y corto, pero hay que tener cuidado con:

  • El uso de strtok modifica la cadena de "línea de comando" original, reemplazando los espacios con / 0 delimitadores de final de cadena
  • argv [] termina apuntando a "commandLine", así que no lo modifique hasta que haya terminado con argv [].

El código:

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;

Ahora puede usar argc y argv, o pasarlos a otras funciones declaradas como "foo (int argc, char ** argv)".


Aquí hay una solución para Windows y Unix (probado en Linux, OSX y Windows). Probado con Valgrind y el Dr. Memory .

Utiliza wordexp para sistemas POSIX y CommandLineToArgvW para Windows.

Tenga en cuenta que para la solución de Windows, la mayoría del código está convirtiendo entre char ** y wchar_t ** con la hermosa API de Win32, ya que no hay CommandLineToArgvA disponible (versión ANSI).

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


Desafortunadamente C ++, pero para otros que pueden buscar este tipo de biblioteca, recomiendo:

ParamContainer - analizador de parámetros de línea de comandos fácil de usar

Realmente pequeño y muy fácil.

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

nombre de programa - nombre largo = valor

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

De acuerdo con mi experiencia:

  • muy util y sencillo
  • estable en producción
  • bien probado (por mi)

El glib siempre maravilloso tiene g_shell_parse_args() que suena como lo que estás buscando.

Si no te interesa siquiera citar, esto podría ser una exageración. Todo lo que necesitas hacer es tokenizar, usando espacios en blanco como un token. Escribir una rutina simple para hacer eso no debería tomar mucho tiempo, realmente.

Si no tiene mucha memoria en la memoria, hacerlo en una sola pasada sin reasignaciones debería ser fácil; simplemente asuma que en el peor de los casos, cada segundo carácter es un espacio, asumiendo que una cadena de n caracteres contiene a lo sumo (n + 1) / 2 argumentos, y (por supuesto) como máximo n bytes de texto de argumento (excluyendo los terminadores).


El LIBTINYC de Matt Peitrek tiene un módulo llamado argcargv.cpp que toma una cadena y la analiza en la matriz de argumentos, teniendo en cuenta los argumentos citados. Tenga en cuenta que es específico de Windows, pero es bastante simple, por lo que debería ser fácil moverse a cualquier plataforma que desee.



Si la solución glib es excesiva para su caso, puede considerar codificar uno.

Entonces tú puedes:

  • escanea la cadena y cuenta cuántos argumentos hay (y obtienes tu argc)
  • asigne una matriz de caracteres * (para su argv)
  • vuelva a escanear la cadena, asigne los punteros en la matriz asignada y reemplace los espacios con ''/ 0'' (si no puede modificar la cadena que contiene los argumentos, debe duplicarla).
  • ¡No olvides liberar lo que has asignado!

El siguiente diagrama debe aclarar (con suerte):

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

Una posible API podría ser:

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

Necesitará consideraciones adicionales para implementar freeparsedargs () de forma segura.

Si su cadena es muy larga y no desea escanear dos veces, puede considerar alternativas como asignar más elementos para las matrices argv (y reasignarlas si es necesario).

EDITAR: Solución propuesta (no manejar el argumento citado).

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


Terminé escribiendo una función para hacer esto por mí mismo, no creo que sea muy bueno, pero funciona para mis propósitos. No dude en sugerir mejoras para cualquier persona que necesite esto en el 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; }


Solución para aquellos que no quieren usar la asignación de memoria dinámica (por ejemplo, incrustada)

tokenise_to_argc_argv() para un proyecto incrustado, que usa strtok_r() como base para convertir una cadena de comandos en forma argc y argv. A diferencia de la mayoría de las respuestas aquí, normalmente asigno la memoria de forma estática. Por lo tanto, mi implementación asume que tienes un límite superior de argv_length . Para la mayoría de las aplicaciones incrustadas típicas, esto es más que suficiente. Incluí un código de ejemplo a continuación también para que pueda usarlo rápidamente.

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:

  • El búfer de caracteres proporcionado debe ser modificable (como strtok_r () inserta /0 en el búfer para delimitar los tokens de cadena).
  • strtok_r en esta función está usando " " carácter de espacio como el único eliminador. Esto emula el comportamiento main(int argc, char *argv[]) en las interfaces típicas de la línea de comandos.
  • Esta función no utiliza malloc o calloc, en lugar de eso, tendrá que asignar la matriz argv por separado y proporcionar la longitud de argv explícitamente. Esto se debe a que pretendo usar esto en dispositivos integrados y, por lo tanto, preferiría asignarlo manualmente.
  • Se utiliza strtok_r() porque es seguro para subprocesos (ya que strtok strtok() usa un puntero estático interno). También es parte de la string.h biblioteca C estándar. Por lo tanto, es muy portátil.

A continuación se muestran el código de demostración, así como su salida. Además, esto muestra que tokenise_to_argc_argv () puede manejar la mayoría de los casos de cadenas y, por lo tanto, ha sido probado. Además, esta función no se basa en malloc o calloc y, por lo tanto, es adecuada para el uso integrado (después de usar los tipos stdint.h ).

Código de demostración

/******************************************************************************* 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]); }

Salida

tokenise_to_argc_argv() Ejemplos

Caso0: Normal

  • Estado inicial
    • Buffer Content tokenising example0 | HEX: 74 6F 6B 65 6E 69 73 69 6E 67 20 65 78 61 6D 70 6C 65 00
  • Después de Tokenizing
    • Contenido de búfer tokenising0example0 | HEX: 74 6F 6B 65 6E 69 73 69 6E 67 00 65 78 61 6D 70 6C 65 00
  • Contenido Argv (argc = 2):
    • argv[0] = tokenising
    • argv[1] = example

Caso 1: Cadena vacía

  • Estado inicial
    • Contenido de Buffer 0 | HEX: 00
  • Después de Tokenizing
    • Contenido de Buffer 0 | HEX: 00
  • Contenido Argv (argc = 0): Argv está vacío

Case2: Extra Space

  • Estado inicial
    • Buffer Content extra space here0 | HEX: 65 78 74 72 61 20 20 73 70 61 63 65 20 68 65 72 65 00
  • Después de Tokenizing
    • Buffer Content extra0 space0here0 | HEX: 65 78 74 72 61 00 20 73 70 61 63 65 00 68 65 72 65 00
  • Contenido Argv (argc = 3):
    • argv[0] = extra
    • argv[1] = space
    • argv[2] = here

Case3: una cadena de palabras

  • Estado inicial
    • Contenido de búfer one-word0 | HEX: 6F 6E 65 2D 77 6F 72 64 00
  • Después de Tokenizing
    • Contenido de búfer one-word0 | HEX: 6F 6E 65 2D 77 6F 72 64 00
  • Contenido Argv (argc = 1):
    • argv[0] = one-word

¿Falta string.h o strtok_r () en su cadena de herramientas de alguna manera?

Si por alguna razón su cadena de herramientas no tiene strtok_r (). Puedes usar esta versión simplificada de strtok_r (). Es una versión modificada de la implementación GNU C de strtok_r (), pero simplificada para soportar solo el carácter de espacio.

Para usar esto, simplemente colóquelo encima de tokenise_to_argc_argv() luego reemplace 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; }