Разбирайте строку в argv / argc

Есть ли способ в C для parsingа fragmentа текста и получения значений для argv и argc, как если бы текст был передан в приложение в командной строке?

Это не нужно работать в Windows, просто Linux – мне также не нужно цитировать аргументы.

Если glib-решение является излишним для вашего дела, вы можете сами рассмотреть его.

Тогда ты можешь:

  • сканировать строку и подсчитать, сколько аргументов (и вы получаете свой argc)
  • выделите массив char * (для вашего argv)
  • пересканировать строку, назначить указатели в выделенном массиве и заменить пробелы на «\ 0» (если вы не можете изменить строку, содержащую аргументы, ее следует дублировать).
  • не забудьте освободить то, что вы выделили!

Диаграмма ниже должна пояснить (надеюсь):

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

Возможным API может быть:

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

Для обеспечения безопасности freeparsedargs () вам понадобятся дополнительные соображения.

Если ваша строка очень длинная, и вы не хотите дважды сканировать, вы можете подумать об альтернативах, таких как выделение большего количества элементов для массивов argv (и перераспределение при необходимости).

EDIT: Предлагаемое решение (дескриптор не указан).

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

Я удивлен, что никто не предоставил самый простой ответ, используя стандартные функции POSIX:

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

Вот мой вклад. Его приятный и короткий, но все, о чем нужно опасаться, это:

  • Использование strtok изменяет исходную строку «commandLine», заменяя пробелы метками «0» конца строки
  • argv [] заканчивается тем, что указывает на «commandLine», поэтому не изменяйте его, пока не закончите с argv [].

Код:

 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; 

Теперь вы можете использовать argc и argv или передать их другим функциям, объявленным как «foo (int argc, char ** argv)».

Всегда замечательный glib имеет g_shell_parse_args() который звучит так, как вы.

Если вас не интересует даже цитирование, это может быть излишним. Все, что вам нужно сделать, это tokenize, используя пробел в качестве символа-символа. Написание простой процедуры для этого не должно занимать много времени, на самом деле.

Если вы не супер-скупой по памяти, делать это за один проход без перераспределения должно быть легко; просто предположим, что наихудший случай каждого второго символа является пространством, поэтому предполагается, что строка из n символов содержит не более (n + 1) / 2 аргументов и (конечно) не более n байтов текста аргумента (исключая терминаторы).

Вот решение для Windows и Unix (протестировано на Linux, OSX и Windows). Протестировано Valgrind и Dr. Memory .

Он использует wordexp для систем POSIX и CommandLineToArgvW для Windows.

Обратите внимание, что для решения Windows большая часть кода преобразует char ** и wchar_t ** с красивым API Win32, поскольку нет доступных CommandLineToArgvA (версия ANSI).

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

Я просто сделал это для встроенного проекта в простой C, где у меня есть небольшой CLI, который разбирает ввод последовательного порта и выполняет ограниченный набор команд с параметрами.

Это, вероятно, не самый аккуратный, но такой маленький и эффективный, как я мог его получить:

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

тестировать:

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

У LIBTINYC Мэтта Пейтрека есть модуль под названием argcargv.cpp, который берет строку и анализирует ее на массив аргументов с учетом приведенных аргументов. Обратите внимание, что это зависит от Windows, но это довольно просто, поэтому вам должно быть легко перемещаться на любую платформу, которую вы хотите.

В итоге я написал функцию, чтобы сделать это сам, я не думаю, что это очень хорошо, но это работает для моих целей – не стесняйтесь предлагать улучшения для всех, кому это нужно в будущем:

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

Решение для тех, кто не хочет использовать распределение динамической памяти (например, встроенный)

Я написал tokenise_to_argc_argv() для внедренного проекта, который использует strtok_r() в качестве основы для токенизации командной строки в argc и argv-форме. В отличие от большинства ответов здесь я обычно выделяю память статически. Таким образом, моя реализация предполагает, что у вас есть верхняя граница argv_length . Для большинства типичных встроенных приложений этого более чем достаточно. Я также включил пример кода ниже, чтобы вы могли быстро его использовать.

 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 } 

Заметка:

  • Предоставляемый буфер символов должен быть модифицируемым (поскольку strtok_r () вставляет \0 в буфер, чтобы разграничить токены строки).
  • strtok_r в этой функции в настоящее время использует символ пробела как единственный разделитель. Это эмулирует main(int argc, char *argv[]) поведение main(int argc, char *argv[]) в типичных интерфейсах командной строки.
  • Эта функция не использует malloc или calloc, вместо этого вам придется выделять массив argv отдельно и явно указывать длину argv. Это связано с тем, что я намерен использовать это во встроенных устройствах и, следовательно, скорее выделил бы его вручную.
  • strtok_r() используется потому, что он является streamовым (поскольку strtok() использует внутренний статический указатель). Также он является частью стандартной библиотеки C. string.h поэтому он очень портативен.

Ниже приведен демонстрационный код, а также его вывод. Кроме того, это показывает, что tokenise_to_argc_argv () может обрабатывать большинство строк и, таким образом, был протестирован. Также эта функция не полагается на malloc или calloc и поэтому подходит для встроенного использования (после использования типов stdint.h ).


Демонстрационный код

 /******************************************************************************* Tokenise String Buffer To Argc and Argv Style Format Brian Khuu 2017 *******************************************************************************/ #include  // printf() #include  // isprint() #include  // 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]); } 

Вывод

tokenise_to_argc_argv() Примеры

Случай 0: Нормальный

  • Начальное состояние
    • tokenising example0 содержимого tokenising example0 | HEX: 74 6F 6B 65 6E 69 73 69 6E 67 20 65 78 61 6D 70 6C 65 00
  • После Tokenizing
    • Содержимое буфера tokenising0example0 | HEX: 74 6F 6B 65 6E 69 73 69 6E 67 00 65 78 61 6D 70 6C 65 00
  • Содержание Argv (argc = 2):
    • argv[0] = tokenising
    • argv[1] = example

Случай1: пустая строка

  • Начальное состояние
    • Содержимое буфера 0 | HEX: 00
  • После Tokenizing
    • Содержимое буфера 0 | HEX: 00
  • Содержание Argv (argc = 0): Argv Is Empty

Случай 2: дополнительное пространство

  • Начальное состояние
    • Содержимое буфера extra space here0 | HEX: 65 78 74 72 61 20 20 73 70 61 63 65 20 68 65 72 65 00
  • После Tokenizing
    • Содержимое буфера extra0 space0here0 | HEX: 65 78 74 72 61 00 20 73 70 61 63 65 00 68 65 72 65 00
  • Содержание Argv (argc = 3):
    • argv[0] = extra
    • argv[1] = space
    • argv[2] = here

Случай 3: одна строка слов

  • Начальное состояние
    • Содержимое буфера one-word0 | HEX: 6F 6E 65 2D 77 6F 72 64 00
  • После Tokenizing
    • Содержимое буфера one-word0 | HEX: 6F 6E 65 2D 77 6F 72 64 00
  • Содержание Argv (argc = 1):
    • argv[0] = one-word

Отсутствует string.h или strtok_r () в вашей инструментальной цепочке?

Если по какой-то причине ваша toolchain не имеет strtok_r (). Вы можете использовать эту упрощенную версию strtok_r (). Это модифицированная версия реализации GNU C strtok_r (), но упрощенная для поддержки только символа пространства.

Чтобы использовать это, просто поместите его поверх tokenise_to_argc_argv() затем замените strtok_r( NULL, " ", &buffer) на 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; } 

К сожалению, C ++, но для других, которые могли бы искать такую ​​библиотеку, я рекомендую:

ParamContainer – простой в использовании синтаксический анализатор параметров командной строки

Действительно маленький и очень простой.

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

имя_программы –long-name = value

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

По моему опыту:

  • очень полезно и просто
  • стабильный на производстве
  • хорошо проверенный (мной)

Рассмотрим еще одну реализацию. Выполнить .

 #include  //  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 

Код использования

 #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' ) // or more clear `strlen(rest)==0` but not efficient cout<<"There is still something to parse. argc_MAX is too small."< 

Вывод:

 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 
  • Разница между параметром и аргументом
  • Есть ли разница между foo (void) и foo () в C ++ или C?
  • getopt не анализирует необязательные аргументы для параметров
  • Как передать аргументы командной строки программе Node.js?
  • Как установить значения по умолчанию для параметров функций в Matlab?
  • C # Передача функции в качестве аргумента
  • Можно ли передать свойства как параметры «out» или «ref»?
  • когда использовать const и const ссылку в функции args
  • Значение ошибки , если (квадратные скобки)
  • Создание аргументов java-метода в качестве окончательного
  • В чем разница между _tmain () и main () в C ++?
  • Interesting Posts

    максимальный размер пакета для TCP-соединения

    Управление музыкальным плеером по умолчанию для Android или любого другого музыкального плеера

    Как можно изменить настройки отображения nvidia?

    Notepad ++ как скопировать текст (только), который соответствует выражению регулярного выражения (не всей строки)

    Каковы известные способы хранения древовидной структуры в реляционной БД?

    GroupingError: ERROR: столбец должен отображаться в предложении GROUP BY или использоваться в агрегатной функции

    Правильный способ утилиты Image / Bitmap и PictureBox

    Поиск массивов «не найден», даже если он найден

    Событие AuthenticateRequest

    Подclass NSObject в Swift: hash vs hashValue, isEqual vs ==

    Проверка MVC3 – требуется от группы

    Неверное строковое значение: ‘\ xF0 \ x9F \ x8E \ xB6 \ xF0 \ x9F …’ MySQL

    Когда и почему базы данных объединяются дорого?

    Сортировка таблицы jQuery

    Загрузить значение измерения из res / values ​​/ dimension.xml из исходного кода

    Давайте будем гением компьютера.