C 忽略字符串中的空格,除非它为';引用

C 忽略字符串中的空格,除非它为';引用,c,strtok,C,Strtok,如果字符串的一部分被引号包围,我很难理解如何保持空格。例如,如果我希望execvp()执行以下命令:diff“space name.txt”sample.txt,它应该将diff保存在args[0],space name.txt保存在args[1],sample.txt保存在args[2] 我真的不确定如何实现它,我尝试了几种不同的if语句逻辑方法,但我不太清楚。目前,我正试图做一些简单的事情,比如:ls“folder”,但是,它陷入了打印printf()语句的while循环 我知道这并不是一个

如果字符串的一部分被引号包围,我很难理解如何保持空格。例如,如果我希望execvp()执行以下命令:diff“space name.txt”sample.txt,它应该将diff保存在args[0],space name.txt保存在args[1],sample.txt保存在args[2]

我真的不确定如何实现它,我尝试了几种不同的if语句逻辑方法,但我不太清楚。目前,我正试图做一些简单的事情,比如:ls“folder”,但是,它陷入了打印printf()语句的while循环


我知道这并不是一个问题——它更多地解释了我试图实现的目标以及目前为止我达到的目标,但我遇到了一些问题,我真的很想知道逻辑应该是怎样的。

而不是使用
strtok
逐字符处理字符串。如果您看到一个
,请设置一个标志。如果标志已设置,请将其取消设置。如果您看到一个空格,请检查标志并切换到下一个参数,或将空格添加到当前。任何其他字符-添加到当前。零字节-完成处理


通过一些额外的努力,你甚至可以处理像
diff“file”“one”“file”“two
(你应该得到
diff
file“one”和
file two
作为结果)

我甚至不明白你想做什么。你想把输入字符串标记成空格分隔的标记吗

只需在空格中分隔输入字符串,当遇到双引号字符时,需要第二个处理引号字符串的内部循环

带引号的字符串不仅仅是搜索结束引号。您需要处理反斜杠,例如反斜杠转义引号和反斜杠转义反斜杠

请考虑以下内容:

char *args[32];
char **next = args;
char *temp = NULL;
char *quotes = NULL;

temp = strtok(line, " \n&");

while (temp != NULL) {
    if (strncmp(temp, "\"", 1) == 0) {
        //int i = strlen(temp);
        printf("first if");
        quotes = strtok(temp, "\"");    
    } else if (strncmp(temp, "\"", 1) != 0) {
        *next++ = temp;
        temp = strtok(NULL, " \n&");
    }
}
它引用了一个带有引号的(无用的)文件名
space name\
。将此作为一个测试用例,这样您就知道什么时候您已经完成了基本操作。请注意,shell命令行拆分要比这疯狂得多。

以下是我的想法:

  • 创建两个指针
    A
    B
    ,最初指向字符串的第一个字符
  • 使用指针
    A
    遍历字符串,将每个字符复制到数组中,只要它不是空格
  • 到达a
    ”后,从位置
    a+1
    开始,将指针
    B
    向前移动,直到到达下一个
    ,复制所有内容,包括空格
  • 现在从数字2开始重复,从char
    B+1
    开始
  • 只要尚未到达
    \0
    ,请重复此操作
  • <强>注:如果存在<强>嵌套< /强>引号,则必须考虑如何做。


    您还可以使用一个标志(int 1 | | 0)和一个指针来表示您是否在引号中,遵循基于标志的两个单独规则。

    编写三个函数。所有这些都应该返回它们处理的字节数。首先,处理引用的参数的那个

    diff "space name \" with quotes.txt\\" foo
    
    。。。然后是一个处理无引号参数的函数:

    size_t handle_quoted_argument(char *str, char **destination) {
        assert(*str == '\"');
        /* discard the opening quote */
        *destination = str + 1;
    
        /* find the closing quote (or a '\0' indicating the end of the string) */
        size_t length = strcspn(str + 1, "\"") + 1;
        assert(str[length] == '\"'); /* NOTE: You really should handle mismatching quotes properly, here */
    
        /* discard the closing quote */
        str[length] = '\0';
        return length + 1;
    }
    
    。。。然后是一个处理(可能重复)空白的函数:

    size_t handle_unquoted_argument(char *str, char **destination) {
        size_t length = strcspn(str, " \n");
        char c = str[length];
        *destination = str;
        str[length] = '\0';
        return c == ' ' ? length + 1 : length;
    }
    
    将这三者结合起来应该很简单:

    size_t handle_whitespace(char *str) {
        int whitespace_count;
        /* This will count consecutive whitespace characters, eg. tabs, newlines, spaces... */
        assert(sscanf(str, " %n", &whitespace_count) == 0);
        return whitespace_count;
    }
    

    通过将其分解为四个独立的算法,您能看到这项任务变得多么简单吗?

    因此,我在这行中读到:

    size_t n = 0, argv = 0;
    while (line[n] != '\0') {
        n += handle_whitespace(line + n);
        n += line[n] == '\"' ? handle_quoted_argument(line + n, args + argv++)
                             : handle_unquoted_argument(line + n, args + argv++);
    }
    
    然后我要说的是:(我没有添加我的初始值设定项,不过你明白了)

    length=strlen(qtemp);
    对于(i=0;i

    我发现使用strcspn很有帮助,正如可修改的左值所建议的那样。

    这个:
    if(strncmp(temp,“\”,1)==0)
    可以写
    if(*temp==”)
    ;它可能会被认为更简短,更容易理解。除非编译器足够聪明,能够优化函数调用,否则它很可能会更快。您可以设置一些标志,当满足第一个引号字符时,将启用该标志,当满足第二个引号字符时,将关闭该标志。当标志处于打开状态时,将空格作为字符串的一部分。您是否考虑过使用
    strchr
    和/或
    strcspn
    而不是
    strtok
    ?是的,我可以看出strcspn和拆分所有内容对我所做的工作更有利。为什么要使用断言?这是我第一次在示例中看到它。@CaerLean
    assert
    有两个用途:提供自我文档(
    assert(*str='\'”);
    说“我断言第一个字符将始终是
    '\''
    ”),并且作为调试辅助工具(在调试运行时断言失败时,您将得到一条错误消息)。我之所以在这里写这个注释,是因为
    assert
    是为了表达不应该发生的事情,并告诉您什么时候会发生,而不是错误处理。注释旁边的断言应替换为正确的错误处理,以通知用户有不匹配的引号。@caerulen-Henc
    while((qtemp = fgets(line, size, stdin)) != NULL ) {
        if (strcmp(line, "exit\n") == 0) {
            exit(EXIT_SUCCESS);
        }
        spaceorquotes(qtemp);
    }    
    
    length = strlen(qtemp);
    for(i = 0; i < length; i++) {
        position = strcspn(qtemp, " \"\n");
        while (strncmp(qtemp, " ", 1) == 0) {   
            memmove(qtemp, qtemp+1, length-1);
            position = strcspn(qtemp, " \"\n");
        } /*this while loop is for handling multiple spaces*/
    
        if (strncmp(qtemp, "\"", 1) == 0) { /*this is for handling quotes */    
            memmove(qtemp, qtemp+1, length-1);
            position = strcspn(qtemp, "\"");
            stemp = malloc(position*sizeof(char));
            strncat(stemp, qtemp, position);
            args[i] = stemp;
        } else {      /*otherwise handle it as a (single) space*/
            stemp = malloc(position*sizeof(char));
            strncat(stemp, qtemp, position); 
            args[i] = stemp;
        }
        //printf("args: %s\n", args[i]);
        length = strlen(qtemp);
        memmove(qtemp, qtemp+position+1, length-position);
    }
    args[i-1] = NULL; /*the last position seemed to be a space, so I overwrote it with a null to terminate */
    if (execvp(args[0], args) == -1) {
        perror("execvp");
        exit(EXIT_FAILURE);
    }