如何检查字符串是否符合格式“ printf like-%d / ...”

时间:2019-06-14 10:01:37

标签: c string stdio

我有"/users/5/10/fnvfnvdjvndfvjvdklchsh"这样的动态字符串,还有"/users/%u/%d/%s"这样的动态格式,如何检查这些字符串是否匹配?

作为字符串,我的意思是char[255]char* str = malloc(x)

我尝试使用sscanf,但我不知道参数和类型的数量,如果我这样做:

int res = sscanf(input, format);

我有堆栈溢出,或者我可以分配堆栈来防止这种情况吗? 这样的示例:

void* buffer = malloc(1024);
int res = sscanf(input, format, buffer);

我想要这样的功能:

bool stringMatches(const char* format, const char* input);

stringMatches("/users/%u/%d/%s", "/users/5/10/fnvfnvdjvndfvjvdklchsh"); //true
stringMatches("/users/%u/%d/%s", "/users/5/10"); //false
stringMatches("/users/%u/%d/%s", "/users/-10/10/aaa"); //false %u is unsigned

您看到任何解决方案了吗?
预先感谢。

3 个答案:

答案 0 :(得分:3)

我认为标准库中没有类似scanf的匹配函数,因此您必须编写自己的匹配函数。复制scanf行为的所有细节很困难,但可能没有必要。

如果只允许%,并且只选择有限的单一格式标识符,而没有尺寸,宽度和精度信息,则代码并不复杂:

bool stringMatches(const char *format, const char *input)
{
    while (*format) {
        if (*format == '%') {
            format++;

            switch(*format++) {
            case '%': {
                    if (*input++ != '%') return false;
                }
                break;

            case 'u': 
                    if (*input == '-') return false;
                    // continue with 'd' case

            case 'd': {                
                    char *end;

                    strtol(input, &end, 0);
                    if (end == input) return false;
                    input = end;
                }
                break;

            case 's':  {
                    if (isspace((uint8_t) *input)) return false;

                    while (*input && !isspace((uint8_t) *input)) input++;
                }
                break;

            default: 
                    return false;
            }
        } else {
            if (*format++ != *input++) return false;
        }
    }

    return (*input == '\0');
}

一些注意事项:

  • 我已经用strtol解析了数字。如果要包括浮点数格式,则在嵌入式系统提供的情况下,可以使用strtod。 (您也可以将isdigit()个字符的延伸部分解析为有效数字。)
  • 这里的'u'情况可以归结为'd'情况。函数strtoul解析一个无符号的long,但是它允许一个负号,因此该情况​​被明确捕获。 (但是,它被捕获的方式将不允许前导空格。)
  • 您可以实现自己的格式或重新解释现有的格式。例如,您可以决定不希望数字前导空格或字符串以斜杠结尾。

答案 1 :(得分:1)

这是一个相当棘手的问题。我认为C没有非常有用的内置函数会对您有所帮助。

您可以做的是使用正则表达式。像这样:

#include <sys/types.h>
#include <regex.h>
#include <stdio.h>

int main(void)
{
    regex_t regex;

    if (regcomp(&regex, "/users/[[:digit:]]+", 0)) {
        fprintf("Error\n");
        exit(1);
    }

    char *mystring = "/users/5/10/fnvfnvdjvndfvjvdklchsh";

    if( regexec(&regex, myString, 0, NULL, 0) == 0)
        printf("Match\n");
}

上面的代码中的正则表达式不适合您的示例。我只是用一些东西来展示这个想法。我认为它将与格式字符串"/users/%u"相对应,但是我不确定。尽管如此,我认为这是解决此问题的最简单方法之一。

答案 2 :(得分:0)

最简单的方法是尝试使用sscanf对其进行解析,然后查看扫描是否成功。

char * str = "/users/5/10/fnvfnvdjvndfvjvdklchsh";

unsigned int tmp_u;
int tmp_d;
char tmp_s[256];

int n = sscanf (str, "/users/%u/%d/%s", &tmp_u, &tmp_d, tmp_s);

if (n!=3)
{
   /* Match failed */
}

请记住,您不必一劳永逸。您可以使用%n格式说明符来获取已解析的字节数,并为下一次解析增加字符串。

此示例滥用了以下事实:如果解析未达到bytes_parsed说明符,将不会修改%n

char * str = "/users/5/10/fnvfnvdjvndfvjvdklchsh";
int bytes_parsed = 0;

/* parse prefix */ 
sscanf("/users/%n", &bytes_parsed);
if (bytes_parsed == 0)
{
  /* Parse error */
}
str += bytes_parsed; /* str = "5/10/fnvfnvdjvndfvjvdklchsh"; */

bytes_parsed = 0;

/* Parse next num */
unsigned int tmp_u
sscanf(str, "%u%n", &tmp_u, &bytes_parsed);
if (bytes_parsed)
{
  /* Number was an unsigned, do something */
}
else 
{
  /* First number was not an `unsigned`, so we try parsing it as signed */
  unsigned int tmp_d
  sscanf(str, "%d%n", &tmp_d, &bytes_parsed);
  if (bytes_parsed)
    {
       /* Number was an unsigned, do something */
    }
}
if (!bytes_parsed)
{
   /* failed parsing number */
}

str += bytes_parsed; /* str = "/10/fnvfnvdjvndfvjvdklchsh"; */

......