将字符串解析为argv/argc

cod*_*box 30 c arguments

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

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

R..*_*R.. 29

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

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


小智 14

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

  • 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;
Run Code Online (Sandbox Code Playgroud)

您现在可以使用argc和argv,或将它们传递给声明为"foo(int argc,char**argv)"的其他函数.


Rem*_*o.D 12

如果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] ________________/ 
Run Code Online (Sandbox Code Playgroud)

可能的API可能是:

    char **parseargs(char *arguments, int *argc);
    void   freeparsedargs(char **argv);
Run Code Online (Sandbox Code Playgroud)

您需要其他注意事项才能安全地实现freeparsedargs().

如果您的字符串很长并且您不想扫描两次,则可以考虑使用替代方法,例如为argv数组分配更多元素(如果需要,还可以重新分配).

编辑:提出的解决方案(des not handle引用的参数).

    #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);
    }
Run Code Online (Sandbox Code Playgroud)

  • 因为getopt做了不同的工作.它需要一组参数并在其中查找选项.这个问题是关于将一​​串"参数"拆分成一个char*数组,这是getopt无法做到的 (5认同)
  • *(提前挑剔)* 请注意,为了符合标准 `argc`/`argv` 布局,缺少一件小事情:`argv` 中最后一个有效条目后面的条目始终设置为 `NULL` ( `"foo bar"`: `argv[0]` -&gt; `"foo"`, `argv[1]` -&gt; `"bar"`, `argv[2]` -&gt; `NULL`)。 (2认同)

unw*_*ind 9

永远在线的精彩油嘴g_shell_parse_args()这听起来像你以后.

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

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


Joa*_*kim 7

这是Windows和Unix的解决方案(在Linux,OSX和Windows上测试).与Valgrind记忆博士一起测试.

它使用wordexp用于POSIX系统,而CommandLineToArgvW用于Windows.

请注意,对于Windows的解决方案,大部分代码之间转换char **,并wchar_t **与美丽的Win32 API的,因为没有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;
}
Run Code Online (Sandbox Code Playgroud)