Обработка перегрузки с помощью scanf () - PullRequest
0 голосов
/ 05 ноября 2018

С помощью scanf () я хотел бы убедиться, что ничего, кроме того, что именно говорит формат, не вводится. Мой код:

if (scanf("< %ld ; %ld > %c", &lo, &hi, &control) == 3)
    {

    }

Я сканирую ввод в форме <0;100> s и хотел бы убедиться, что никто не может ввести что-то вроде <0;100> sasdf (представляющее интервал), в этом случае scanf () говорит, что это нормально, потому что он сделал 3 успешных преобразований, а не бросать ошибки. Я действительно хочу сканировать только один символ после интервала. Как мне этого добиться?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

В общем, я согласен , что лучше использовать fgets для построчного считывания в буфер и синтаксического анализа буфера, вместо использования scanf непосредственно для stdin. В частности, scanf оставляет непрочитанные данные, которые могут вызвать проблемы в несвязанных местах, пытаясь прочитать что-то еще.

Однако, если вы настроены на scanf, возможно, вы захотите что-то вроде:

if (scanf("< %ld ; %ld > %c%1[^\n ]%*[^\n ]", &lo, &hi, &control, &control) == 3)

То есть попробуйте прочитать один непробельный и не символ новой строки после управляющего символа. Если это удается, возвращается 4 вместо 3, в результате чего сравнение == 3 будет ложным. Подавленный (*) ввод в конце затем потребляет любой не-перевод строки и ввод без пробела.

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

0 голосов
/ 05 ноября 2018

Считайте ввод как строку, используя, например, fgets(). Затем используйте sscanf(), чтобы попытаться разобрать его:

char  buffer[1024];
char *line;
long  lo, hi;
char  control, dummy;

line = fgets(buffer, sizeof buffer, stdin);
if (!line) {
    /* No input at all. */
    exit(EXIT_FAILURE);
}

if (sscanf(line, " < %ld ; %ld > %c %c", &lo, &hi, &control, &dummy) == 3) {
    /* Format is good, we have lo, hi, and control. */
} else {
    /* Not this format; complain. */
}

Обратите внимание, что окончательному %c должен предшествовать пробел; таким образом, любые конечные пробелы, включая символы новой строки, используются до финального %c. По сути, это фиктивное преобразование, которое не происходит при правильном формате; именно поэтому мы ожидаем результат 3, а не 4. Когда результат равен 3, мы знаем, что первые три преобразования были успешными, но четвертое фиктивное не было, и это, очевидно, означает, что ввод был отформатирован правильно, в желаемом шаблон, без чего-либо еще на линии.

Таким образом, вы также можете поддерживать несколько различных форматов одновременно. Например,

if (sscanf(line, " < %ld ; %ld > %c %c", &lo, &hi, &control, &dummy) == 3 ||
    sscanf(line, " ( %ld %1*[,;] %ld ) %c %c", &lo, &hi, &control, &dummy) == 3 ||
    sscanf(line, " %ld %ld %c %c", &lo, &hi, &control, &dummy) == 3) {
    /* Format is good, we have lo, hi, and control. */
} else {
    /* Not this format; complain. */
}

принимает, например, <1;2>x, < 1 ; 2 > x, (3,4)y, (5;6)z, 7+8w и 9 10 c.

* * * * * * Часть 10 * * - это что-то напуганное: 1 говорит, что можно принять не более одного символа. Звездочка * говорит, что она не считается преобразованием (в возвращаемом значении) и не сохраняется (поэтому нет соответствующего параметра). Символ модификатора типа [ указывает список принятых (или отклоненных, если ^ является первым символом) символов до ]. Итак, на английском языке мы можем описать это так: «принять одну запятую или точку с запятой, но не считать это преобразованием или хранить его» * ​​1028 *. (Если бы это было %3*[,;/], то было бы "принимать от одного до трех символов, где каждый символ - запятая, точка с запятой или косая черта" . Очень полезно для такого рода случаев.)

...