如何用分号解析命令行? [英] How can I parse the command line with semicolons?

查看:88
本文介绍了如何用分号解析命令行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试读取由分号分隔的命令行参数,该分号前后带有空格(例如ls; date; cal),但是分隔部分很困难。当我简单地放置一个单独的命令行(例如 ls date )时,我的代码就起作用了,但是无论何时我放分号,它不起作用(例如 ls; date



这是我的C代码:

  void parse(char * userInput,char ** splitInput)
{
//读取直到没有userInput行尾
while(* userInput!='\0')
{
//将userInput中的任何空格替换为'\0'
while(* userInput = =';')
{
* userInput ++ ='\0';
}
//保存参数位置
* splitInput ++ = userInput;
//如果userinput不等于空格,则读取下一个userInput
while(* userInput!=''&& * userInput!=';'&& * userInput!='\ \0')
{
userInput ++;
}
}
}

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

if(pid> 0)//父进程
{
pid_t parent_pid;
parent_pid = wait(NULL);
}
else if(pid == 0)//子进程
{
if(execvp(* splitInput,splitInput)< 0)
{
printf(%s:找不到命令\n,* splitInput);
出口(1);
}
}
else //错误
{
perror( fort error);
}
}

void main(void)
{
char userInput [100]; // execvp的第一个参数
char * splitInput [100]; // execvp的第二个参数

while(strcmp(userInput, quit)!= 0)
{
//询问用户输入
printf(组10>);
//读取输入的整行
scanf(%[^ \n],userInput);
//从输入中获取字符;停止循环问题
getchar();
//如果用户输入等于退出,则退出
if(strcmp(userInput, quit)== 0)
{
exit(0);
}
//解析输入
parse(userInput,splitInput);
//执行分叉
execute(splitInput);
}
}


解决方案

有有很多方法可以做到这一点。 string.h 提供了几个可以使用的功能, strtok() strsep() strchr() strcspn()和<$ c $的组合c> strspn()取决于您的需求。您还可以始终在字符串中移动指针,从字符串中选择所需的标记,而忽略空格和包含多个分隔符。从教育的角度来看,以这种方式进行学习实际上具有很好的指针学习价值。



每当您遍历任何内容时,都可以选择各种内容,而不是尝试包含多个内容嵌套的 while 循环,每个循环都旨在向前扫描以跳过或查找特定类别的字符,因此使用 state循环通常更有利,您可以使用多个标志之一来跟踪不同的状态。 (行内单词阅读字符,或单词之间的分隔符或空格等。)。这样就不需要嵌套循环,您只需使用一个循环就可以循环循环每个字符,具体取决于您当前的状态。



将其投入工作以扫描您的状态字符串,并选择由定界符';'或空格终止的每个单词,并在其中保留单个状态标志 int; 来跟踪您是在单词中读取字符( in = 1; )还是在单词处理空格和定界符之间( in = 0 ; )并使用 char * sp 作为指向每个单词开头和<$ c的起始指针 $ c> userInput 作为指向正在读取的当前字符的终结点,您可以执行以下操作:

  void parse(char * userInput,char ** splitInput,char delim,size_t nptrs)
{
int = 0; / *简单字标记0-false / 1-true * /
size_t n = 0; / *用于保护splitInput边界的计数器* /
char * sp = userInput; / *起始指针已初始化为userInput * /

而(n< nptrs-1){/ *循环,而指针仍未填充* /
/ *如果在结尾处为空格或一个分隔符* /
if(!* userInput || isspace(* userInput)|| * userInput == delim){
if(in){/ * if in word * /
splitInput [n ++] = sp; / *设置指向起始指针的指针* /
splitInput [n] = NULL; / *设置下一个指针NULL * /
}
in = 0; / *在标志零中复位* /
if(* userInput)/ *如果空格或delim,nul终止* /
* userInput = 0;
else / *否则* /
返回; / *字符串结尾* /
}
else {/ *普通字符* /
if(!in){/ *如果非单词* /
sp = userInput; / *将起始指针设置为1st好字符* /
in = 1; / *将字标设置为true * /
}
}
userInput ++; / *前进至下一个字符* /
}
}

注意: delim 字符上方与 nptrs 一起作为参数传递,以传递您可以使用的指针,因此可以在填充指针时保护指针数组的边界,还请注意,该函数始终将数组中的下一个指针设置为 NULL 作为哨兵,使您可以在 main()中循环遍历数组中的指针,直到达到 NULL ,因为您不返回指针,用作函数返回或通过指针参数)



一个简单的示例,它解析 my;;; dog; ;; has; fleas; 使用';'或空白作为分隔符可以是:

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

#define NPTR 32 / *如果需要一个常量,#define一个(或多个)* /

void parse(char * userInput,char ** splitInput,char delim,size_t nptrs)
{
int = 0; / *简单字标记0-false / 1-true * /
size_t n = 0; / *用于保护splitInput边界的计数器* /
char * sp = userInput; / *起始指针已初始化为userInput * /

而(n< nptrs-1){/ *循环,而指针仍未填充* /
/ *如果在结尾处为空格或一个分隔符* /
if(!* userInput || isspace(* userInput)|| * userInput == delim){
if(in){/ * if in word * /
splitInput [n ++] = sp; / *设置指向起始指针的指针* /
splitInput [n] = NULL; / *设置下一个指针NULL * /
}
in = 0; / *在标志零中复位* /
if(* userInput)/ *如果空格或delim,nul终止* /
* userInput = 0;
else / *否则* /
返回; / *字符串结尾* /
}
else {/ *普通字符* /
if(!in){/ *如果非单词* /
sp = userInput; / *将起始指针设置为1st好字符* /
in = 1; / *将字标设置为true * /
}
}
userInput ++; / *前进至下一个字符* /
}
}

int main(void){

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

解析(s,拆分,;,NPTR);

而(* p)
printf('%s’\n,* p ++);
}

注意:标头<$ c $包含c> ctype.h 是为了使用 isspace()函数测试空格,而不是将串在一起if()语句检查空间'\t'或<$ c

示例使用/输出

  $ ./bin/split_ptr_arr3 
'my'
'dog'
'has '
'跳蚤'

上面所有包含的空格中的输出注释已被删除。



仔细检查一下,如果您有任何疑问,请告诉我。字面上有很多方法可以分割字符串,这只是一种常见且基本的方法。


I am trying to read the command line arguments that are separated by a semicolon with a blank space front and back (such as ls ; date ; cal), but the separation part has been difficult. My codes are working when I simply put an individual command line (such as ls or date), but whenever I put the semicolon, it does not work (such as ls ; date)

Here is my C code:

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

解决方案

There are a number of ways to do this. string.h provides several functions that can be used, strtok(), strsep(), strchr(), or a combination of strcspn() and strspn() depending on your needs. You can also always walk-a-pointer down the string picking out the wanted tokens from the string and ignoring whitespace and multiple-included-delimiters. There is actually good pointer learning value in approaching it this way from an education standpoint.

Any time you are looping over anything picking out various pieces, instead of trying to include multiple nested while loops, each designed to scan-forward to either skip or find a certain class of characters, it is often more advantageous to use a state-loop where you use one of more flags to keep track of differing states. (line in-word reading character, or between words reading delimiters or whitespace, etc..). That way no nested loops are required and you simply use a single loop to loop over each character responding accordingly depending on your current state.

Putting that in work to scan down your string and pick out each of the words terminated by a delimiter ';' or by whitespace, and keeping a single state flag int in; to track whether you are in-word reading characters (in = 1;) or between words handling spaces and delimiters (in = 0;) and use char *sp as the start-pointer pointing to the beginning of each word and userInput as the end-pointer pointing to the current character being read, you could do:

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

(note: above the delim character is passed as a parameter along with nptrs to pass the number of pointers you have available so you can protect your pointer array bounds while filling pointers. Also note, the function always sets the next pointer in your array to NULL as a sentinel allowing you to loop over the pointers in your array in main() until NULL is reached since you don't return the number of pointers used, either as the function return or through a pointer parameter)

A simple example that parses the words from " my; ; ; dog ;;; has;fleas ;" using ';' or whitespace as delimiters could be:

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

(note: the header ctype.h is included to make use of the isspace() function to test for whitespace rather than stringing together if() statements checking for space, '\t' or '\n'directly. This is generally good practice.)

Example Use/Output

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

Note in the output above all included whitespace is removed.

Look things over and let me know if you have questions. There are literally dozens of way to approach splitting strings, this is just one common and basic approach.

这篇关于如何用分号解析命令行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆