Как использовать постоянную #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);
Другие вопросы по тегам