Для гибкости в разборе ввода, предпочитайте fgets()
для выборки строк ввода и sscanf()
для анализа этого ввода. Это позволяет при необходимости повторять сканирование на одном и том же входе.
Для использования fgets()
необходим буфер ввода. Функция fgets()
принимает аргумент, описывающий размер этого входного буфера, и считывает не более одного символа меньше из входного потока. В случае, если пользовательский ввод превышает этот предел, дополнительный поток остается во входном потоке. Это может вызвать проблемы для последующих операций ввода-вывода, поэтому надежный код должен обрабатывать эти дополнительные символы (считывая и используя или отбрасывая их). Более простой код, который не нуждается в надежности, может просто выбрать буфер размера, достаточно большой, который вряд ли будет перегружен доверенными пользователями. Приведенный ниже код выбирает размер буфера BUFSIZE
равным 1024, что должно быть достаточно для дополнительного места, даже если пользователь случайно вводит слишком много символов в конце ввода. Также обратите внимание, что при наличии места для этого символ \n
, обозначающий конец строки, считывается из входного потока и сохраняется во входном буфере. Эту новую строку часто нужно удалять из буфера, но в случаях, связанных с sscanf()
, часто никаких дополнительных действий не требуется, поскольку новая строка не должна мешать сканированию.
Функция sscanf()
(фактически все fscanf()
семейные функции) отвечают на директиву %n
, которая записывает количество символов, считанных с ввода до появления этой директивы, в предоставленную целочисленную переменную. Для sscanf()
это фактически означает, что когда в строке формата встречается %n
, индекс символа, следующего за последним прочитанным символом, сохраняется в этой предоставленной целочисленной переменной.
В приведенном ниже коде используется это %n
средство, сначала сделав num_assigned = sscanf(buffer, "%9s %d%n", value1, &value2, &scan_loc)
. Здесь сканируются первые два значения, которые всегда ожидаются, а количество символов, считанных до этой точки, сохраняется в scan_loc
. Переменная num_assigned
содержит количество назначений, сделанных sscanf()
, которое должно быть 2. Если это не так, то программа завершает работу с сообщением об ошибке; в противном случае код продолжается, пытаясь проанализировать остальную часть входной строки. Здесь buffer
указывает на первый символ массива buffer[]
, а buffer + scan_loc
указывает на символ , следующий за последним символом, прочитанным sscanf()
. То есть второй вызов на sscanf()
начинается там, где закончился первый вызов. Если этот второй вызов успешно делает присвоение value3
, то num_assigned
увеличивается.
Обратите внимание, что sscanf()
может возвращать 0 или -1, если назначение не выполнено, в зависимости от обстоятельств. По этой причине код, подобный следующему: num_assigned += sscanf(buffer + scan_loc, "%d", &value3)
не годится, поскольку невыполнение назначения может привести к уменьшению num_assigned
. Также обратите внимание, что fgets()
не подвержен проблемам переполнения буфера, если указан правильный размер входного буфера. Если buffer
является массивом, тогда sizeof buffer
всегда будет обеспечивать правильный размер (если бы вместо этого buffer
был указателем, размер должен был бы быть получен другим способом). Но sscanf()
все еще подвержен переполнению буфера при использовании директивы %s
. Всегда указывайте максимальную ширину поля при использовании %s
(или, в этом случае, директивы scanset %[]
), чтобы избежать переполнения буфера, когда ввод больше ожидаемого. Терминатор \0
- это всегда , записываемый sscanf()
с директивой %s
, поэтому для использования пространства %9s
используется массив типа char value1[10]
с емкостью для 10 символов.
#include <stdio.h>
#include <stdlib.h> // for exit() and EXIT_FAILURE
#define BUFSIZE 1024
int main(void) {
char value1[10];
int value2 = 0;
int value3 = 0;
char buffer[BUFSIZE]; // generous user input buffer
int scan_loc = 0; // scan location in buffer
int num_assigned = 0; // number of values assigned by sscanf()
// fetch user input: parse only if fgets is successful
if (fgets(buffer, sizeof buffer, stdin) != NULL) {
// attempt to make first two assignments
num_assigned = sscanf(buffer, "%9s %d%n", value1, &value2, &scan_loc);
if (num_assigned == 2) {
// success: attempt to make third assignment
int final_assignment = sscanf(buffer + scan_loc, "%d", &value3);
if (final_assignment == 1) {
++num_assigned;
}
} else {
fprintf(stderr, "Input error: %d values read\n", num_assigned);
exit(EXIT_FAILURE);
}
printf("value1: %s, value2: %d", value1, value2);
if (num_assigned == 3) {
printf(", value3: %d\n", value3);
} else {
putchar('\n');
}
}
return 0;
}
Образцы казней:
$ ./a.out
test2 42
value1: test2, value2: 42
$ ./a.out
test3 12 34
value1: test3, value2: 12, value3: 34