【问题标题】:How to check string matches format "printf like - %d/..."如何检查字符串匹配格式“printf like - %d/...”
【发布时间】:2019-06-14 10:01:37
【问题描述】:

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

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

【问题讨论】:

  • 你可以使用正则表达式,或者自己解析字符串,如果我必须这样做,我会先检查斜杠'/'的数量,然后读取中间的所有内容。斜线之间的内容不能为空。
  • sscanf 返回已解析字符的计数。您将该计数与长度(输入)进行比较

标签: c string stdio


【解决方案1】:

我认为标准库中没有类似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 解析一个无符号长整数,但它允许一个减号,因此可以显式捕获这种情况。 (但它被捕获的方式,它不允许前导空格。)
  • 您可以实现自己的格式或重新解释现有格式。例如,您可以决定不希望数字前导空格或字符串以斜杠结尾。

【讨论】:

  • case 's' 中的if (isspace((uint8_t) *input)) return false; 不需要比较“printf like”。
【解决方案2】:

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

【讨论】:

  • 好主意,但我需要在嵌入式 (stm32 f4) 中工作的解决方案,库 regex.h 速度是否足够?
  • 如果它对您来说足够快,我无法回答。您必须自己尝试一下。如果不是,请发布一个新问题。
  • 好的,我今晚会检查并回复你:)
【解决方案3】:

最简单的方法是尝试使用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"; */

......

【讨论】:

  • sscanf (str, "/users/%u/%d/%s", &amp;tmp_u, &amp;tmp_d, tmp_s); 正如我所说,这是不可能的:我不知道参数的数量,格式在外面。
  • 所以你的格式字符串没有在编译时给出?然后您应该自己解析格式字符串,并使用 sscanf(s, "%u%n", ...)sscanf(s, "%d%n",...) 等遍历您的输入字符串,具体取决于原始格式字符串中的说明符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
  • 1970-01-01
  • 2014-09-03
  • 1970-01-01
  • 1970-01-01
  • 2020-01-04
  • 2010-12-19
相关资源
最近更新 更多