将字符串解析为argv / argc

时间:2009-11-10 09:09:04

标签: c arguments

在C中是否有办法解析一段文本并获取argv和argc的值,就像文本已经在命令行中传递给应用程序一样?

这不一定适用于Windows,只需要Linux - 我也不关心引用参数。

13 个答案:

答案 0 :(得分:28)

我很惊讶没有人使用标准POSIX功能提供最简单的答案:

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

答案 1 :(得分:15)

这是我的贡献。它很好而且很短,但需要注意的是:

  • 使用strtok修改原始的“commandLine”字符串,用\ 0 end-of-string delimeters替换空格
  • 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)”的其他函数。

答案 2 :(得分:11)

如果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数组分配更多元素(并在需要时重新分配)。

编辑:建议的解决方案(不处理引用的参数)。

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

答案 3 :(得分:9)

始终精彩的glibg_shell_parse_args(),听起来就像你所追求的那样。

如果你对引用甚至不感兴趣,这可能有点矫枉过正。您需要做的就是使用空格作为标记字符进行标记。写一个简单的例程来做到这一点不应该花很长时间,真的。

如果你对记忆并不吝啬,那么在没有重新分配的情况下一次性完成它应该很容易;假设每个第二个字符都是空格的最坏情况,因此假设一串n个字符包含最多(n + 1) / 2个参数,并且(当然)最多n个字节的参数文本(不包括终结者)。

答案 4 :(得分:6)

这是Windows和Unix的解决方案(在Linux,OSX和Windows上测试)。使用ValgrindDr. Memory进行了测试。

它对POSIX系统使用wordexp,对Windows使用CommandLineToArgvW

请注意,对于Windows解决方案,大多数代码都使用漂亮的Win32 API在char **wchar_t **之间进行转换,因为没有CommandLineToArgvA可用(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;
}

答案 5 :(得分:3)

我刚刚为普通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);
}

答案 6 :(得分:2)

Matt Peitrek的LIBTINYC有一个名为argcargv.cpp的模块,它接受一个字符串并将其解析到带有引用参数的参数数组中。请注意,它是特定于Windows的,但它非常简单,因此应该很容易移动到您想要的任何平台。

答案 7 :(得分:2)

我最后写了一个函数来自己做这个,我觉得它不是很好,但它适用于我的目的 - 随意建议改进其他需要这个的人:

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

答案 8 :(得分:1)

考虑另一种实现方式。 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;
};

使用代码

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

输出:

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

答案 9 :(得分:1)

那些不想使用动态内存分配的解决方案(例如嵌入式)

我为嵌入式项目编写了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()示例

案例0:正常

  • 初始状态
    • 缓冲内容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

案例1:空字符串

  • 初始状态
    • 缓冲内容0 | HEX:00
  • 标记后
    • 缓冲内容0 | HEX:00
  • Argv内容(argc = 0):Argv为空

案例2:额外空间

  • 初始状态
    • 缓冲内容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):
    • argv[0] = one-word

以某种方式在工具链中缺少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;
}

答案 10 :(得分:1)

我写的这个也考虑了引号(但不是嵌套的)

请随时贡献。

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

示例输出:

$ ./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

答案 11 :(得分:1)

我的项目需要将字符串分成argcargv

找到了一个非常出色的代码Torek。但是它改变了输入缓冲区,所以我做了一些修改以满足我的需求。

在命令行中输入时,我只花了一点点处理引号混合,因此行为更(不完全)像Linux Shell。

注意:此功能不会编辑原始字符串,因此您可以重复使用输入缓冲区(错误报告等)。

void remove_quote(char* input){
   //Implementing yourself to remove quotes so it would be completely like Linux shell
}
size_t cmd_param_split(char *buffer, char *argv[], size_t argv_max_size)
{
    char *p, *start_of_word;
    int c, i;
    enum states { DULL=0, IN_WORD, IN_STRING, QUOTE_DOUBLE,QUOTE_SINGLE } state = DULL;
    size_t argc = 0;
    int quote = 0;
    for (p = buffer; argc < argv_max_size && *p != '\0'; p++) {
        c = (unsigned char) *p;
        printf("processing %c, state = %d\n", c,state);
        switch (state) {
        case DULL:
            if (isspace(c)) {
                continue;
            }

            if (c == '"' ||c == '\'') {
                quote = c;
                state = IN_STRING;
                start_of_word = p + 1;
                continue;
            }
            state = IN_WORD;
            start_of_word = p;
            continue;

        case IN_STRING:
            if (c == '"' || c == '\'') {
                if (c!=quote)
                    continue;
                else
                    quote = 0;
                strncpy(argv[argc],start_of_word, p - start_of_word);
                remove_quote(argv[argc]);
                argc++;
                state = DULL;
            }
            continue;

        case IN_WORD:
            if(quote==0 && (c == '\"' ||c == '\''))
                quote = c;
            else if (quote == c)
                    quote = 0;

            if (isspace(c) && quote==0) {
                strncpy(argv[argc],start_of_word, p - start_of_word);
                remove_quote(argv[argc]);
                argc++;
                state = DULL;
            }
            continue;
        }
    }

    if (state != DULL && argc < argv_max_size){
        strncpy(argv[argc],start_of_word, p - start_of_word);
        remove_quote(argv[argc]);
        argc++;
    }

    if (quote){
        printf("WARNING: Quote is unbalanced. This could lead to unwanted-behavior\n");
        for(i = 0;i<argc;i++)
            printf("arg %d = [%s]\n",i,argv[i]);
        printf("Original buffer: [%s]\n",buffer);
    }
    return argc;
}

int main()
{
    int i=0;
    int argc;
    char* argv[64];
    for(i=0;i<64;i++){
        argv[i] = malloc(256);
        memset(argv[i],0x0,256);
    }
     char* buffer="1 2 3 \'3 4\"567\' \"bol\'obala\" 2x2=\"foo\"";
    argc = cmd_param_split(buffer,argv,64);
    for(i = 0;i<argc;i++)
        printf("arg %d = [%s]\n",i,argv[i]);

    return 0;
}

使用以下字符串进行了测试

 1. "1 2 3 \'3 4\"567\' \"bol\'obala\" 2x2=\"foo\""
   arg 0 = [1]
   arg 1 = [2]
   arg 2 = [3]
   arg 3 = [3 4"567]
   arg 4 = [bol'obala]
   arg 5 = [2x2="foo"]
 2. "./foo bar=\"Hanoi HoChiMinh\" exp='foo123 \"boo111' mixquote \"hanoi \'s\""
   arg 0 = [./foo]
   arg 1 = [bar="Hanoi HoChiMinh"]
   arg 2 = [exp='foo123 "boo111']
   arg 3 = [mixquote]
   arg 4 = [hanoi 's]

但是,在RaspberryPi中测试的cmd行中运行时,Linux shell会删除引号,即使是混合大小写,如下所示。

./foo bar="Hanoi HoChiMinh" exp='foo123 "boo111' mixquote "hanoi 's"
   arg 0 = [./foo]
   arg 1 = [bar=Hanoi HoChiMinh]
   arg 2 = [exp=foo123 "boo111]
   arg 3 = [mixquote]
   arg 4 = [hanoi 's]

因此,如果您真的想模仿整个Linux Shell的行为,那么在上面保留空白时,只需花一点点力气即可删除引号remove_quote()函数。

答案 12 :(得分:0)

不幸的是C ++,但对于其他可能会搜索此类库的人,我建议:

ParamContainer - easy-to-use command-line parameter parser

真的很小很容易。

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

programname --long-name = value

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

根据我的经验:

  • 非常有用和简单
  • 稳定生产
  • 经过良好测试(由我)