如何用分号解析命令行?

How can I parse the command line with semicolons?

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

这是我的 C 代码:

void parse(char *userInput, char **splitInput)
{
  //read until userInput is not end of line
  while (*userInput != '[=10=]')
  {
    //replace any space in userInput as '[=10=]'
    while (*userInput == ';')
    {
      *userInput++ = '[=10=]';
    }
    //save the argument position
    *splitInput++ = userInput;
    //if userinput is not equal to space, read next userInput
    while (*userInput != ' ' && *userInput != ';' && *userInput != '[=10=]')
    {
      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);
  }
}

有多种方法可以做到这一点。 string.h提供了几个可以使用的函数,strtok()strsep()strchr(),或者strcspn()strspn()的组合,看你的需要.您也可以始终 walk-a-pointer 沿着字符串从字符串中挑选出想要的标记并忽略空格和 multiple-included-delimiters。从教育的角度来看,以这种方式接近它实际上有很好的指针学习价值。

任何时候你在循环任何东西时挑选出不同的部分,而不是试图包含多个嵌套的 while 循环,每个循环都设计为 scan-forward 跳过或找到特定的 class 的字符,使用 state-loop 通常更有利,您可以在其中使用多个 flags 来跟踪不同的状态. (第 in-word 行读取字符,或在单词之间读取定界符或空格等。)。这样就不需要嵌套循环,您只需使用一个循环来遍历每个字符,根据您当前的状态做出相应的响应。

将其用于扫描您的字符串并挑选出每个以定界符 ';' 或空格终止的单词,并保留一个状态标志 int in; 以跟踪您是否in-word 读取字符 (in = 1;) 或在单词之间处理空格和分隔符 (in = 0;) 并使用 char *sp 作为 start-pointer 指向每个单词的开头, userInput 作为 end-pointer 指向当前正在读取的字符,你可以这样做:

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 作为标记,允许您循环遍历 main() 中数组中的指针,直到 [=达到 28=] 是因为您没有 return 使用的指针数量,无论是作为函数 return 还是通过指针参数)

一个使用 ';' 或空格作为分隔符解析来自 " 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++);
}

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

例子Use/Output

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

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

检查一下,如果您有任何问题,请告诉我。拆分字符串的方法有很多种,这只是一种常见的基本方法。