sscanf - различное количество аргументов формата? - PullRequest
4 голосов
/ 30 декабря 2011

В моей программе я использую sscanf, чтобы проверить, имеет ли строка заданный формат.Для этого я предоставляю количество аргументов в строке формата и проверяю, возвращает ли sscanf это же число при синтаксическом анализе ввода.

Как часть анализатора примитивов, я хочу проверить, соответствует ли строкаодин из многих форматов.Функция sscanf является вариативной, так как мне справиться с переменным количеством аргументов, которые мне нужно передать?

В настоящее время я просто передаю очень большое количество аргументов (например, 50) в функцию, ипросто надеюсь, что строки формата не содержат больше аргументов.

Есть ли лучший способ сделать это?

Ответы [ 6 ]

3 голосов
/ 30 декабря 2011

Вам действительно нужно что-то более тяжелое, чем 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
0 голосов
/ 30 декабря 2011

Если вы не знаете , когда пишете код число и тип (ы) аргументов, sscanf() не может безопасно делать то, что вы пытаетесь сделать.

Передача 50 аргументов в sscanf() в порядке (аргументы, не используемые строкой формата, оцениваются, но в противном случае игнорируются), но аргументы, соответствующие строке формата, должны быть ожидаемого типа после повышения;в противном случае поведение не определено.Поэтому, если вы хотите определить, может ли строка быть отсканирована с помощью "%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, но найти реализацию достаточно просто).

0 голосов
/ 30 декабря 2011

Вы можете написать функцию проверки, используя аргументы переменной длины, используя макросы, доступные в 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 голосов
/ 30 декабря 2011

Бесполезный ответ: «Не делайте этого, напишите парсер правильно, возможно, используя lex и / или yacc или bison».

Ответ на заданный вами вопрос: «Да, вы могли бы сделать это». Я не верю, что есть какая-то причина, по которой не может быть больше параметров с переменными параметрами, чем требует формат, хотя немногим было бы плохо. Я предполагаю, что у вас есть массив или список возможных форматов, и вы вызываете sscanf в цикле.

0 голосов
/ 30 декабря 2011

Я не уверен, что он отвечает на ваш вопрос, но вы используете varargs в C , чтобы разрешить переменной количество аргументов для функции.

void myscanf(const char *fmt, ...)
{
}
0 голосов
/ 30 декабря 2011

Вам, вероятно, следует использовать lex / yacc для создания правильного парсера.Кроме того, первая маркировка строки с помощью strtok может упростить вашу проблему.(Осторожно: правильно использовать strtok действительно сложно - внимательно прочитайте документацию.)

...