【问题标题】:Parsing Input in C revisited重新审视 C 中的解析输入
【发布时间】:2017-12-28 07:02:03
【问题描述】:

我有一个"a b.c d.e"(不包括引号)形式的输入,我想解析成整数值,例如a1=a, a2=b, a3=c,..等等。但诀窍是"." 之后的值也可能丢失,因此"a b d.e""a b c""a b.c d" 是有效输入。但是"a b. d.e" 无效,因为在"b" 之后有一个"." 但后面没有数字。假设数字即。 "a", "b"等是大于0小于1000的整数。

我只能想办法做到这一点。试过这个 ->

scanf("%d %2d[^. ].%2d[^. ] %2d[^. ].%2d[^\n]",&a,&b,&c,&d,&e);

但它没有工作。感谢您使用scanf() 参数技巧解析它的任何帮助。

【问题讨论】:

  • 请选择你的毒药 - C 或 C++ - 两种不同的语言
  • 应该%2d%3d
  • 也许给我们一些不起作用的示例输入会有所帮助
  • 使用适当的词法分析器和解析器或正则表达式库。请避免使用scanf
  • 正确阅读 strtok 的描述,您可以同时对两者进行标记“.”,然后使用 ptr2 = strtok(0," .");找到下一个命中。

标签: c parsing user-input scanf


【解决方案1】:

如果我理解您对我的评论的回答,那么在这种情况下,我认为您不能将输入硬塞到 scanf格式字符串 并处理您想要处理的变体。但是,在这种情况下,您可以回退到经过验证的真实情况,解析任何内容,strtol 的帮助下沿着字符串向下移动指针

strtol 提供了一个 end-pointer,它在输入字符串中有效转换为 long 后设置为下一个字符。因此,如果您正在处理复杂的输入字符串,请从strtol (p, &ep, 10) 开始,其中p 是指向您的字符串的指针,ep 将在第一个数字转换后设置为字符串中的下一个字符. (在您的情况下是'.'' ')。如果下一个字符是'.',您可以对后面的字符进行进一步测试,并在逐个收集值时确定您的输入字符串是否有效。

在您测试所需条件并将ep 前进以指向下一个要转换的有效数字的开头后,您只需设置p = ep; 并重复直到填满a, b & c 或遇到错误条件。

使用strtol 时,除了检查errno 之外,您还可以使用几个额外的验证,但这对于本示例来说就足够了。其余验证请参见man strtol

如果我正确理解了您的逻辑,您可以执行类似以下的操作:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define MAXC 32

int main (void) {

    char buf[MAXC] = "", *p = buf, *ep = NULL;
    int a = 0, b = 0, c = 0, tmp = 0;

    printf ("enter values: ");
    if (!fgets (buf, MAXC, stdin))
        return 1;

    for (;;) {
        tmp = strtol (p, &ep, 10);
        if (errno) {    /* minimal conversion validation */
            fprintf (stderr, "error: failed conversion.\n");
            return 1;
        }
        if (!a)         /* handle a */
            a = tmp;
        else if (!b) {  /* handle b */
            if (*ep && *ep == '.' && *(ep + 1) &&  *(ep + 1) == ' ') {
                fprintf (stderr, "error: invalid input = '.' alone.\n");
                return 1;
            }
            b = tmp;
            if (*ep && *ep == '.' && *(ep + 1) && 
                '0' <= *(ep + 1) && *(ep + 1) <= '9')
                ep++;
            else
                while (*ep && (*ep < '0' || '9' < *ep))
                    ep++;
            if (!*ep) {
                fprintf (stderr, "error: invalid input - no 'c'.\n");
                return 1;
            }
        }
        else if (!c) {  /* handle c */
            if (*ep && *ep == '.' && *(ep + 1) && 
                (*(ep + 1) == ' ' || *(ep + 1) == '\n')) {
                fprintf (stderr, "error: invalid input = '.' alone.\n");
                return 1;
            }
            c = tmp;
            break;
        }
        p = ep;
    }

    printf ("a : %d\nb : %d\nc : %d\n", a, b, c);

    return 0;
}

使用/输出示例

$ ./bin/triplet
enter values: 1 2.3 4.5
a : 1
b : 2
c : 3

$ ./bin/triplet
enter values: 1 2 3.4
a : 1
b : 2
c : 3

$ ./bin/triplet
enter values: 1 2 3
a : 1
b : 2
c : 3

$ ./bin/triplet
enter values: 1 2.3 4
a : 1
b : 2
c : 3

$ ./bin/triplet
enter values: 1 2. 3.4
error: invalid input = '.' alone.

如果我误解了您的评论,请仔细查看并告诉我。如果没有,请始终记住,您可以通过简单地沿着字符串遍历一个指针(或一对指针)来解析任何内容。

【讨论】:

  • 您对转换失败的测试是不完整的:您应该检查 th long 值是否超出int 的范围以及是否ep &gt; p
  • 同意,这就是为什么我在代码本身中明确指出/* minimal conversion validation */ 并解释了“使用strtol 时,除了检查errno 之外,您还可以使用几个额外的验证,” i> 在答案本身中引用man strtol 进行其余验证,例如ERANGE(tmp == LONG_MIN || tmp == LONG_MAX) 用于 溢出p == ep 用于无数字
【解决方案2】:

没有简单的方法可以将scanf() 用于您的目的。如果您想对输入进行严格验证,则必须编写一个量身定制的解析器。

这里有一个解决方案:

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

char *skipblanks(char *p) {
    while (isblank((unsigned char)*p)
        p++;
    return p;
}

int getnumber(char *p, char **pp, int *dest) {
    if (strchr("0123456789+-", *p) {
        long val;
        errno = 0;
        val = strtol(p, pp, 10);
        if (errno == 0 && *pp > p && val >= INT_MIN && val <= INT_MAX) {
            *dest = val;
            return 1;
        }
    }
    return 0;
}

int parseinput(char *p, int *a, int *b, int *c, int *d, int *e)) {
    return getnumber(p, &p, a)
       &&  (*p != '.' || getnumber(p + 1, &p, b))
       &&  isblank((unsigned char)*p)
       &&  getnumber(skipblanks(p), &p, c))
       &&  (*p != '.' || getnumber(p + 1, &p, d))
       &&  isblank((unsigned char)*p)
       &&  getnumber(skipblanks(p), &p, e))
       &&  *skipblanks(p) == '\n';
}

int main(void) {
    char buf[128];
    int a, b, c, d, e;
    int status;
    char *p;

    if (fgets(buf, sizeof buf, stdin)) {
        /* set default values for b and d */
        a = b = c = d = e = 0;
        if (parseinput(buf, &a, &b, &c, &d, &e)) {
            printf("a=%d, b=%d, c=%d, d=%d, e=%d\n, a, b, c, d, e);
        } else {
            printf("input format error\n");
        }
    } else {
       printf("no input\n");
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 2020-05-21
    • 2010-09-18
    • 2015-04-22
    • 1970-01-01
    相关资源
    最近更新 更多