如何在格式错误的输入中正确使用带有循环的sscanf?

时间:2018-07-10 03:24:58

标签: c stdin

我正在尝试从stdin中读取用于堆栈数据结构的命令,有效的命令是“ 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之后有数字,但在pop之后没有数字,因此,这行代码对于“ 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);
    }

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:2)

您的使用中没有明显的错误,但是有许多细微的错误导致您遇到问题。

您面临的主要问题是在转换字符串和整数之后仅获得一个偏移量。对于"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

使用/输出示例

验证案例:

$ 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

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:0)

如果大小恒定,则可以在" "char **中用char tab[][]作为分隔符来分隔行。

然后,您将检查tab[i]是否为数字,然后根据需要使用它,或者如果命令(tab[i+1])后跟数字,则该命令将为«push»,否则为«pop»»