Как использовать постоянную #defined в качестве максимальной ширины поля в fscanf?
Все это в C89, а не в C99.
У меня есть постоянная.
#define MAX_NAME_LEN 256
Я хочу использовать его в качестве максимальной ширины поля в fscanf, вроде этого.
fscanf(input, "%256s", name);
Но я хочу использовать MAX_NAME_LEN вместо литерала 256 ради хорошего стиля. Я перепробовал все
fscanf(input, "%MAX_NAME_LENs", name);
char* max_name_len_str = malloc(16 * sizeof *max_name_len_str);
sprintf(max_name_len_str, "%d", MAX_NAME_LEN);
fscanf(input, "%" max_name_len_str "s", name);
free(max_name_len_str);
//works with printf, but has different meaning in scanf
fscanf(input, "%*s", MAX_NAME_LEN, name);
fscanf(input, "%%ds", MAX_NAME_LEN, name);
безуспешно.
char* nameFormat = malloc(16 * sizeof *nameFormat); //I assume I don't ever want more than 10^13 characters in a name
sprintf(nameFormat, "%s%ds", "%", MAX_NAME_LEN);
fscanf(input, nameFormat, name);
free(nameFormat);
работает, но неуклюже как все вылезают. Есть ли более элегантное решение?
4 ответа
1-е упрощение.
// Unlikely need to malloc short array
char nameFormat[16]; // I assume I don't ever want more than 10^13 characters
sprintf(nameFormat, "%%%ds", MAX_NAME_LEN); // %% prints a %
fscanf(input, nameFormat, name);
2-е предложение или лучше, используйте stringify @DevilaN.
Обратите внимание, что размер буфера должен быть как минимум на 1 больше, чем "%s"
ширина.
#define MAX_NAME_LEN 256
#define MAX_NAME_LEN_STR "256"
char name[MAX_NAME_LEN + 1];
fscanf(input, "%" MAX_NAME_LEN_STR "s", name);
В-третьих, использовать fgets()
(мое предпочтение), конечно, это читает строку, а не последовательность символов, не являющихся пробелами. Это часто соответствует этой цели более высокого уровня.
#define MAX_NAME_LEN 256
char name[MAX_NAME_LEN + 2];
fgets(name, sizeof name, input);
Вы можете использовать этот макрос:
#define STRINGIFY(X) INDIRECT(X)
#define INDIRECT(X) #X
как это:
#define MAX 10
puts("%"STRINGIFY(MAX)"d");
который напечатает %10d
,
В вашем случае это будет
char name[MAX_NAME_LEN + 1];
fscanf(input, "%"STRINGIFY(MAX_NAME_LEN)"s", name);
#
в макросе преобразует (делает "
вокруг этого) что бы ни было после этого. Таким образом, макрос должен быть только десятичным числом.
Уровень косвенности необходим для расширения MAX_NAME_LEN
в 256
, INDIRECT(MAX_NAME_LEN)
будет расширяться до "MAX_NAME_LEN"
,
Вы будете бороться с fscanf. Я не знаю лучшего решения, чем предложенное вами, если вы связаны с fscanf.
Лично я бы переключился на fgets как в этом простом примере:
#define MAX_NAME_LEN 25
int main()
{
char string[MAX_NAME_LEN];
fgets(string,MAX_NAME_LEN,stdin);
printf("%s", string);
}
В качестве ссылки, ниже приводится fscanf_s()
подход для предотвращения переполнения, который использовался в более ранней версии VS, которая не достигала соответствия C99. Вдобавок unsigned
Аргумент следует name[]
индикатор размера буфера. Это относится и к VS 2015 тоже.
char name[MAX_NAME_LEN + 1];
fscanf_s(input, "%s", name, (unsigned) sizeof name);
C11 включает в себя fscanf_s()
в Приложении K, которое является нормативным, только для информации, поэтому совместимый компилятор не должен его реализовывать. Тем не менее, в C11 это указано как нуждающийся в rsize_t/size_t
аргумент.
fscanf_s(input, "%s", name, sizeof name);
Если введено более 256 символов без пробелов, происходит сбой сопоставления. В этом случае, fscanf()
вернуть EOF
, Я думаю, что версия VS делает то же самое. Я думаю, что оба будут использовать все незаполненное пространство в потоке, даже если недостаточно места для их хранения.
Любой из них немного отличается от приведенного ниже тем, что дополнительный текст не читается name[]
, поэтому нет переполнения буфера. Любой дополнительный текст остается для следующего вызова функции ввода. fscanf()
возвращает 1.
fscanf(input, "%256s", name);