C 忽略字符串中的空格,除非它为';引用
如果字符串的一部分被引号包围,我很难理解如何保持空格。例如,如果我希望execvp()执行以下命令:diff“space name.txt”sample.txt,它应该将diff保存在args[0],space name.txt保存在args[1],sample.txt保存在args[2] 我真的不确定如何实现它,我尝试了几种不同的if语句逻辑方法,但我不太清楚。目前,我正试图做一些简单的事情,比如:ls“folder”,但是,它陷入了打印printf()语句的while循环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循环 我知道这并不是一个
我知道这并不是一个问题——它更多地解释了我试图实现的目标以及目前为止我达到的目标,但我遇到了一些问题,我真的很想知道逻辑应该是怎样的。而不是使用
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开始重复,从charB+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和拆分所有内容对我所做的工作更有利。为什么要使用断言?这是我第一次在示例中看到它。@CaerLeanassert
有两个用途:提供自我文档(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);
}