sscanf - 格式参数数量不固定?

4
在我的程序中,我使用sscanf来检查一个字符串是否符合给定的格式。为此,我提供了在格式字符串中参数的数量,并检查当解析输入时sscanf返回相同的数量。
作为一个基本的解析器,我想检查一个字符串是否与多个格式之一匹配。由于sscanf函数是可变参的,所以我如何处理需要传递的不同数量的参数?
目前,我只是向函数传递了非常大的参数(例如50),并希望格式字符串不包含更多的参数。
有没有更好的方法来处理这个问题?

@Hogan:据我所知,正则表达式不是C标准库的一部分。 - ryyst
1
@ryyst 它们不是标准的C语言,但它们符合POSIX标准。 - Dave
1
@ryyst:请查看man 3 regexec - Dan Fego
@ryyst - 那怎么样?这是解决问题的最佳方法,使用正则表达式库并感到高兴。据我所知,您没有说您受限于仅使用标准C库。 - Hogan
听起来像是词法分析。sscanf 不太适用,建议使用正则表达式。 - Pubby
6个回答

3
你真正需要的是比scanf更重的工具。你必须告诉scanf输入的格式是什么,因为它无法自己推断出来。
如果你可以访问POSIX,请查看regex.h,这可能是你所需要的一切。
否则,你只能自己动手写。如果格式相当复杂,则lex和yacc很好用,但是如果不是很复杂,则strtok或(getchar+switch)可能是更好的选择。
编辑:由于你可以使用POSIX,在此提供一个简单的示例,展示如何从c中的正则表达式中提取数据。(出于简洁起见,省略了错误检查。)
char txt[] = "232343341235898dfsfgs/.f";
regex_t reg;
regmatch_t refs[MAX_REFS]; //as in, the maximum number of data you want to extract
regcomp(&reg, "3433\\([0-5]*\\).*", 0); //replace 0 with REG_EXTENDED if desired
regexec(&reg, txt, MAX_REFS, refs, 0);
regfree(&reg);

txt[refs[0].rm_eo+1] = '\0';
int n = atoi(txt+refs[0].rm_so);
printf("%d\n", n);

打印
41235

我可以使用POSIX,所以regex.h将是我的下一个选择。我能否像sscanf一样使用regex.h将输入解析为不同的变量,还是我必须同时使用regex.hsscanf - ryyst
你所需要的只有regexec。我将添加一个示例。 - Dave

1

您可以使用stdarg.h中提供的宏,使用可变长度参数编写验证函数。

例如,

int my_validation_func(const char *format, ...) {
    va_list ap;
    char *p, *sval;
    int ival;
    float fval;

    va_start(ap, format);
    for(p=format; *p ; p++) {
        if (*p != '%') {
            continue;
        }
        switch(*++p) {
            case 'd':
                ival = va_arg(ap, int);
                break;

            case 'f':
                fval = va_arg(ap, float);
                break;

            case 's':
                for (sval = va_arg(ap, char *); *sval; sval++);
                break;

            default:
                break;
        }
    }
    va_end(ap);
}

希望这能帮到你!


0

你应该使用lex/yacc来构建一个合适的解析器。或者,首先使用strtok对字符串进行分词可能会简化你的问题。(注意:正确使用strtok非常棘手--请仔细阅读其文档。)


0

不太有用的答案是“不要那样做,正确地编写解析器,可以使用lex和/或yacc或bison”。

你所问的问题的答案是“是的,你可以这样做”。我认为没有任何理由不能有比格式要求更多的可变参数,尽管太少会是一件坏事。我假设你有一个可能格式的数组或列表,并在循环中调用sscanf。


0

我不确定它是否回答了你的问题,但是在C语言中使用varargs可以允许函数接收可变数量的参数。

void myscanf(const char *fmt, ...)
{
}

0

如果你在编写代码时不知道参数的数量和类型,sscanf() 就无法安全地执行你想要的操作。

sscanf() 传递50个参数是可以的(未被格式字符串消耗的参数将被评估但忽略),但是与格式字符串对应的参数必须是预期类型(在提升后);否则,行为是未定义的。所以,如果你想检测一个字符串是否可以使用 "%d""%f" 进行扫描,就不能只用一次 sscanf() 调用安全地完成。(可能可以通过传递指向足够大的缓冲区的 void* 来解决问题,但行为仍然是未定义的。)

sscanf() 的另一个麻烦问题是它不能处理数值溢出。如下:

char *s = "9999999999999999999999999";
int n;
int result = sscanf(s, "%d", &n);
printf("result = %d, n = %d\n", result, n);

具有未定义的行为(假设9999999999999999999999999太大而无法存储在int中)。

你可能能够做的是找到一个开源的sscanf实现,并修改它,使其仅验证字符串与格式是否匹配,而不存储任何内容。(处理实现的许可证留作练习。)如果你发现sscanf风格的格式字符串特别方便解决问题,那么这是有意义的。否则,正则表达式可能是正确的方法(虽然不在C标准中,但很容易找到实现)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接