Как проверить соответствие строки формату "printf like -% d / ..." - PullRequest
0 голосов
/ 14 июня 2019

У меня есть динамическая строка, например: "/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 ]

3 голосов
/ 14 июня 2019

Я не думаю, что в стандартной lib есть 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 анализирует длинную без знака, но она допускает знак минус, так что регистр явно обнаруживается. (Но, как он пойман, он не пропустит пробелы.)
  • Вы можете реализовать свои собственные форматы или заново интерпретировать существующие. Например, вы можете решить, что вы не хотите, чтобы начальные пробелы в числах или строка заканчивалась косой чертой.
1 голос
/ 14 июня 2019

Это довольно сложно. Я не думаю, что 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", но я не уверен. Тем не менее, я думаю, что это один из самых простых способов решения этой проблемы.

0 голосов
/ 14 июня 2019

Самое простое - просто попытаться проанализировать его с помощью 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 не будет изменен, если синтаксический анализ не выполняется.t достигает спецификатора %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"; */

......
...