首页 > 解决方案 > 如何用分号解析命令行?

问题描述

我正在尝试读取由分号分隔且前后有空格的命令行参数(例如 ls ; date ; cal ),但分隔部分很困难。当我简单地输入一个单独的命令行(例如lsor date)时,我的代码可以工作,但是每当我输入分号时,它就不起作用(例如ls ; date

这是我的 C 代码:

void parse(char *userInput, char **splitInput)
{
  //read until userInput is not end of line
  while (*userInput != '\0')
  {
    //replace any space in userInput as '\0'
    while (*userInput == ';')
    {
      *userInput++ = '\0';
    }
    //save the argument position
    *splitInput++ = userInput;
    //if userinput is not equal to space, read next userInput
    while (*userInput != ' ' && *userInput != ';' && *userInput != '\0')
    {
      userInput++;
    }
  }
}

void execute(char **splitInput)
{
  pid_t pid = fork();

  if (pid > 0) //parent process
  {
    pid_t parent_pid;
    parent_pid = wait(NULL);
  }
  else if (pid == 0) //child process
  {
    if (execvp(*splitInput, splitInput) < 0) 
    {
      printf("%s: command not found\n", *splitInput);
      exit(1);
    }      
  }  
  else //error
  {
    perror("fort error");
  }
}

void main(void)
{
  char userInput[100]; //execvp's first argument
  char *splitInput[100]; //execvp's second argument

  while(strcmp(userInput,"quit") != 0)
  {
    //ask for a user input
    printf("group 10> ");
    //read the entire line of input
    scanf("%[^\n]", userInput);
    //get characters from input; stop the loop problem
    getchar();
    //quit if user input is equal to quit
    if (strcmp(userInput, "quit") == 0)
    {
      exit(0);
    }
    //parse the input
    parse(userInput, splitInput);
    //execute fork
    execute(splitInput);
  }
}

标签: ccommand-lineseparator

解决方案


有很多方法可以做到这一点。string.h提供了几个可以使用的功能,strtok(), strsep(), strchr(), 或结合使用strcspn()strspn()具体取决于您的需要。您还可以始终沿着字符串向下移动指针,从字符串中挑选出想要的标记,并忽略空格和多重包含分隔符。从教育的角度来看,以这种方式接近它实际上有很好的指针学习价值。

任何时候你循环任何东西,挑选出不同的部分,而不是尝试包含多个嵌套while循环,每个嵌套循环都设计为向前扫描以跳过或查找特定类别的字符,使用状态循环通常更有利,其中您使用多个标志之一来跟踪不同的状态。(行字内阅读字符,或字之间阅读分隔符或空格等。)。这样就不需要嵌套循环,您只需使用单个循环来循环每个字符,根据您当前的状态做出相应的响应。

将其用于扫描您的字符串并挑选出以分隔符';'或空格结尾的每个单词,并保留一个状态标志int in;来跟踪您是在字内阅读字符(in = 1;)还是在处理空格和分隔符的单词之间(in = 0;) 并char *sp用作指向每个单词开头的起始指针和指向正在读取的当前字符userInput结束指针,您可以这样做:

void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
    int in = 0;             /* simple in-word flag 0-false/1-true */
    size_t n = 0;           /* counter to protect splitInput bounds */
    char *sp = userInput;   /* start-pointer initialized to userInput */

    while (n < nptrs - 1) { /* loop while pointers remain unfilled */
        /* if at end, is whitespace or a delimiter */
        if (!*userInput || isspace(*userInput) || *userInput == delim) {
            if (in) {                   /* if in word */
                splitInput[n++] = sp;   /* set pointer to start-pointer */
                splitInput[n] = NULL;   /* set next pointer NULL */
            }
            in = 0;                     /* reset in flag zero */
            if (*userInput)             /* if space or delim, nul-terminate */
                *userInput = 0;
            else    /* otherwise */
                return;                 /* at end-of-string */
        }
        else {  /* normal char */
            if (!in) {                  /* if not in-word */
                sp = userInput;         /* set start-pointer to 1st good char */
                in = 1;                 /* set in-word flag true */
            }
        }
        userInput++;    /* advance to next char */
    }
}

注意:上面的delim字符作为参数传递,并nptrs传递您可用的指针数量,因此您可以在填充指针时保护指针数组边界。另请注意,该函数始终将数组中的下一个指针设置NULL为sentinel 允许您循环遍历数组中的指针,main()直到NULL到达,因为您不返回使用的指针数量,无论是作为函数返回还是通过指针参数)

" my; ; ; dog ;;; has;fleas ;"一个简单的例子,它解析使用或空格作为分隔符的单词';'可能是:

#include <stdio.h>
#include <ctype.h>

#define NPTR 32     /* if you need a constant, #define one (or more) */

void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
    int in = 0;             /* simple in-word flag 0-false/1-true */
    size_t n = 0;           /* counter to protect splitInput bounds */
    char *sp = userInput;   /* start-pointer initialized to userInput */

    while (n < nptrs - 1) { /* loop while pointers remain unfilled */
        /* if at end, is whitespace or a delimiter */
        if (!*userInput || isspace(*userInput) || *userInput == delim) {
            if (in) {                   /* if in word */
                splitInput[n++] = sp;   /* set pointer to start-pointer */
                splitInput[n] = NULL;   /* set next pointer NULL */
            }
            in = 0;                     /* reset in flag zero */
            if (*userInput)             /* if space or delim, nul-terminate */
                *userInput = 0;
            else    /* otherwise */
                return;                 /* at end-of-string */
        }
        else {  /* normal char */
            if (!in) {                  /* if not in-word */
                sp = userInput;         /* set start-pointer to 1st good char */
                in = 1;                 /* set in-word flag true */
            }
        }
        userInput++;    /* advance to next char */
    }
}

int main (void) {

    char s[] = "  my; ; ;  dog  ;;; has;fleas  ;", *split[NPTR] = { NULL }, **p = split;

    parse (s, split, ';', NPTR);

    while (*p)
        printf ("'%s'\n", *p++);
}

注意:包含标头ctype.h是为了使用该isspace()函数来测试空格,而不是将if()检查的语句串在一起space'\t'或者'\n'直接。这通常是一种很好的做法。)

示例使用/输出

$ ./bin/split_ptr_arr3
'my'
'dog'
'has'
'fleas'

注意在上面的输出中,所有包含的空格都被删除了。

看看事情,如果你有问题,请告诉我。实际上有几十种方法来处理拆分字符串,这只是一种常见和基本的方法。


推荐阅读