Решение для тех, кто не хочет использовать динамическое выделение памяти (например, встроенный)
Я написал 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[])
в типичных интерфейсах командной строки.
- Эта функция не использует malloc или calloc, вместо этого вам придется отдельно выделять массив argv и явно указывать длину argv. Это потому, что я намереваюсь использовать это во встроенных устройствах и, таким образом, скорее выделю это вручную.
strtok_r()
используется потому, что он потокобезопасен (поскольку 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 <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]);
}
выход
tokenise_to_argc_argv()
Примеры
Case0: Normal
- Исходное состояние
- Буферный контент
tokenising example0
| HEX: 74 6F 6B 65 6E 69 73 69 6E 67 20 65 78 61 6D 70 6C 65 00
- После токенизации
- Буферный контент
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
Case1: пустая строка
- Исходное состояние
- Буферный контент
0
| HEX: 00
- После токенизации
- Буферный контент
0
| HEX: 00
- Содержание Argv (argc = 0): Argv Is Empty
Case2: дополнительное пространство
- Исходное состояние
- Буферный контент
extra space here0
| HEX: 65 78 74 72 61 20 20 73 70 61 63 65 20 68 65 72 65 00
- После токенизации
- Буферный контент
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
- После токенизации
- Буферный контент
one-word0
| HEX: 6F 6E 65 2D 77 6F 72 64 00
- Содержание Argv (argc = 1):
Отсутствует string.h или strtok_r () в вашей цепочке инструментов?
Если по какой-то причине в вашем наборе инструментов нет функции strtok_r (). Вы можете использовать эту упрощенную версию strtok_r ().
Это модифицированная версия реализации strtok_r () в GNU C, но упрощенная, чтобы поддерживать только пробел.
Чтобы использовать это, просто поместите его поверх 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;
}