如何在格式错误的输入中正确使用带循环的 sscanf?
How to use sscanf with loops correctly with ill-formatted input?
我正在尝试从标准输入读取堆栈数据结构的命令,有效命令是 "push [number]" 和 "pop",这是我的代码:
while(getline(&input, &len, stdin) > 0){
while ((n = sscanf(input,"%64s%d%n",cmd,&num,&offset)) > 0){
if (n == 1){
if (!strcmp("pop",cmd)){
pop(&head);
} else if (!strcmp("push",cmd)){
//Doing something
} else if (isNumeric(cmd) && need_num){
push(&head, num);
} else{
//error
}
}
else if (n == 2){
if (!strcmp("push",cmd)){
push(&head, num);
} else {
//error
}
}
else {
//error
}
input += offset;
}
}
当然,这个程序有很多缺陷,因为我不熟悉带有循环的 sscanf。第一个问题是,如果我在一行中读入一系列命令,例如:
push 1 push 2 pop push 3
实际上会报错,因为push后面有number,pop后面没有number,这样,这行代码对于"pop"命令是错误的:
input += offset;
但我不知道如何解决这个问题。
另一个问题是,如果我认为将 "push [number]" 命令分成两行是可以接受的:
push 5 pop push
4
我不知道是否有比我所做的更简单的方法来确定该行是否以 push 结束并且以下行以数字开头:
else if (!strcmp("push",cmd)){
//Doing something
} else if (isNumeric(cmd) && need_num){
push(&head, num);
}
如有任何帮助,我们将不胜感激!
如果大小不变,您可以在 char **
或 char tab[][]
中使用 " "
作为分隔符拆分您的行。
然后你会检查你的 tab[i]
是否是一个数字,然后根据需要使用它,或者如果 (tab[i+1])
后面跟着一个数字,则为 « push »,否则为 «弹出 ».
嗯,您在使用过程中没有任何明显的错误,但有一些细微的错误给您带来了问题。
您遇到的主要问题是在转换字符串和整数后只取一个偏移量。在 "pop"
的情况下, 匹配失败 发生在 %d
之前 你到达 %n
您的 格式字符串 导致 n
保持未设置状态(保留最后一个设置值 - 在这种情况下导致 offset
的增量太大)
相反,您需要在格式字符串中使用两个这样的检查,例如:
while ((rtn = sscanf (input, "%63s %n%d %n",
cmd, &off1, &num, &off2)) > 0) {
这样,如果读取 "pop"
,则在 off1
中有适当的偏移量,如果读取 "push num"
,则在 off2
中有适当的偏移量。 (每个 %n
之前的附加空格是可选的(但建议)。使用 %s
和 %d
前导空格被消耗——但最好养成考虑它的习惯,因为%[...]
或 %c
不消耗前导空白。
将它放在一个简短的例子中,你可以做类似下面的事情:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCMD 64 /* max characters for command */
int main (void) {
char *buf = NULL; /* buffer for getline */
size_t n = 0; /* alloc size (0 getline decides) */
ssize_t nchr = 0; /* getline returns (no. of chars read */
while ((nchr = getline (&buf, &n, stdin)) > 0) { /* read each line */
char cmd[MAXCMD] = "", /* buffer for command */
*input = buf; /* pointer to advance */
int num = 0, /* number to push */
off1 = 0, /* offset if single conversion */
off2 = 0, /* offset if double conversion */
rtn = 0; /* sscanf return */
while ((rtn = sscanf (input, "%63s %n%d %n",
cmd, &off1, &num, &off2)) > 0) {
switch (rtn) { /* switch on sscanf return */
case 1: /* handle "pop" case */
if (strcmp (cmd, "pop") == 0) {
printf ("pop\n");
input += off1; /* set offset based on off1 */
}
else
fprintf (stderr, "error: invalid single cmd '%s'.\n",
cmd);
break;
case 2: /* handle "push num" case */
if (strcmp (cmd, "push") == 0) {
printf ("push %d\n", num);
input += off2; /* set offset based on off2 */
}
else
fprintf (stderr, "error: invalid single cmd '%s'.\n",
cmd);
break;
default:
fprintf (stderr, "error: invalid input.\n");
break;
}
}
}
free (buf); /* free memory allocated by getline */
return 0;
}
(注:我使用了switch()
语句,但如果你愿意,可以随意使用if, else if, else
)
示例Use/Output
验证案例:
$ echo "pop pop push 1 push 2 pop push 3 pop" | ./bin/sscanfpushpop
pop
pop
push 1
push 2
pop
push 3
pop
$ echo "push 5 pop push 6 push 7 pop push 8" | ./bin/sscanfpushpop
push 5
pop
push 6
push 7
pop
push 8
检查一下,如果您有任何其他问题,请告诉我。
我正在尝试从标准输入读取堆栈数据结构的命令,有效命令是 "push [number]" 和 "pop",这是我的代码:
while(getline(&input, &len, stdin) > 0){
while ((n = sscanf(input,"%64s%d%n",cmd,&num,&offset)) > 0){
if (n == 1){
if (!strcmp("pop",cmd)){
pop(&head);
} else if (!strcmp("push",cmd)){
//Doing something
} else if (isNumeric(cmd) && need_num){
push(&head, num);
} else{
//error
}
}
else if (n == 2){
if (!strcmp("push",cmd)){
push(&head, num);
} else {
//error
}
}
else {
//error
}
input += offset;
}
}
当然,这个程序有很多缺陷,因为我不熟悉带有循环的 sscanf。第一个问题是,如果我在一行中读入一系列命令,例如:
push 1 push 2 pop push 3
实际上会报错,因为push后面有number,pop后面没有number,这样,这行代码对于"pop"命令是错误的:
input += offset;
但我不知道如何解决这个问题。 另一个问题是,如果我认为将 "push [number]" 命令分成两行是可以接受的:
push 5 pop push
4
我不知道是否有比我所做的更简单的方法来确定该行是否以 push 结束并且以下行以数字开头:
else if (!strcmp("push",cmd)){
//Doing something
} else if (isNumeric(cmd) && need_num){
push(&head, num);
}
如有任何帮助,我们将不胜感激!
如果大小不变,您可以在 char **
或 char tab[][]
中使用 " "
作为分隔符拆分您的行。
然后你会检查你的 tab[i]
是否是一个数字,然后根据需要使用它,或者如果 (tab[i+1])
后面跟着一个数字,则为 « push »,否则为 «弹出 ».
嗯,您在使用过程中没有任何明显的错误,但有一些细微的错误给您带来了问题。
您遇到的主要问题是在转换字符串和整数后只取一个偏移量。在 "pop"
的情况下, 匹配失败 发生在 %d
之前 你到达 %n
您的 格式字符串 导致 n
保持未设置状态(保留最后一个设置值 - 在这种情况下导致 offset
的增量太大)
相反,您需要在格式字符串中使用两个这样的检查,例如:
while ((rtn = sscanf (input, "%63s %n%d %n",
cmd, &off1, &num, &off2)) > 0) {
这样,如果读取 "pop"
,则在 off1
中有适当的偏移量,如果读取 "push num"
,则在 off2
中有适当的偏移量。 (每个 %n
之前的附加空格是可选的(但建议)。使用 %s
和 %d
前导空格被消耗——但最好养成考虑它的习惯,因为%[...]
或 %c
不消耗前导空白。
将它放在一个简短的例子中,你可以做类似下面的事情:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCMD 64 /* max characters for command */
int main (void) {
char *buf = NULL; /* buffer for getline */
size_t n = 0; /* alloc size (0 getline decides) */
ssize_t nchr = 0; /* getline returns (no. of chars read */
while ((nchr = getline (&buf, &n, stdin)) > 0) { /* read each line */
char cmd[MAXCMD] = "", /* buffer for command */
*input = buf; /* pointer to advance */
int num = 0, /* number to push */
off1 = 0, /* offset if single conversion */
off2 = 0, /* offset if double conversion */
rtn = 0; /* sscanf return */
while ((rtn = sscanf (input, "%63s %n%d %n",
cmd, &off1, &num, &off2)) > 0) {
switch (rtn) { /* switch on sscanf return */
case 1: /* handle "pop" case */
if (strcmp (cmd, "pop") == 0) {
printf ("pop\n");
input += off1; /* set offset based on off1 */
}
else
fprintf (stderr, "error: invalid single cmd '%s'.\n",
cmd);
break;
case 2: /* handle "push num" case */
if (strcmp (cmd, "push") == 0) {
printf ("push %d\n", num);
input += off2; /* set offset based on off2 */
}
else
fprintf (stderr, "error: invalid single cmd '%s'.\n",
cmd);
break;
default:
fprintf (stderr, "error: invalid input.\n");
break;
}
}
}
free (buf); /* free memory allocated by getline */
return 0;
}
(注:我使用了switch()
语句,但如果你愿意,可以随意使用if, else if, else
)
示例Use/Output
验证案例:
$ echo "pop pop push 1 push 2 pop push 3 pop" | ./bin/sscanfpushpop
pop
pop
push 1
push 2
pop
push 3
pop
$ echo "push 5 pop push 6 push 7 pop push 8" | ./bin/sscanfpushpop
push 5
pop
push 6
push 7
pop
push 8
检查一下,如果您有任何其他问题,请告诉我。