如何检查字符串匹配格式 "printf like - %d/..."

How to check string matches format "printf like - %d/..."

我有像 "/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

你有什么解决办法吗?
提前致谢。

这是一个相当棘手的问题。我不认为 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" 但我不确定。不过,我认为这是解决此问题的最简单方法之一。

我认为标准库中没有类似 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 == '[=10=]');
}

一些注意事项:

  • 我用 strtol 解析了数字。如果你想包含 floating-point 数字格式,你可以使用 strtod,如果你的嵌入式系统提供的话。 (您还可以将 isdigit() 个字符的延伸部分解析为有效数字。)
  • 'u' 案例在此落入 'd' 案例。函数 strtoul 解析一个 unsigned long,但它允许一个减号,所以这种情况会被显式捕获。 (但是它被捕获的方式,它不允许前导白色space。)
  • 您可以实施自己的格式或 re-interpret 现有格式。例如,您可以决定不希望数字前导白色 space 或者字符串以斜杠结尾。

最简单的就是尝试用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 格式说明符来获取已解析的字节数,并为下一次解析递增字符串。

这个例子滥用了如果解析没有达到 %n 说明符就不会修改 bytes_parsed 的事实:

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"; */

......