简体   繁体   中英

How do I use a #defined constant as the max field width in fscanf?

All of this is in C89, not C99.

I have a constant.

#define MAX_NAME_LEN 256

I want to use it as the max field width in fscanf, sort of like this.

fscanf(input, "%256s", name);

But I want to use the MAX_NAME_LEN instead of the literal 256 for sake of good style. I have tried all of

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);

without success.

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);

does work, but is ungainly as all get out. Is there a more elegant solution?

You can use this macro:

#define STRINGIFY(X) INDIRECT(X)
#define INDIRECT(X) #X

like this:

#define MAX 10
puts("%"STRINGIFY(MAX)"d");

which will print %10d .

In your case it would be

char name[MAX_NAME_LEN + 1];
fscanf(input, "%"STRINGIFY(MAX_NAME_LEN)"s", name);

The # in the macro stringifies (makes " around it) whatever is after it. So the macro must be the decimal number only.
The level of indirection is needed to expand MAX_NAME_LEN to 256 . INDIRECT(MAX_NAME_LEN) would expand to "MAX_NAME_LEN" .

1st simplification.

// 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);

2nd offering or better, use stringify @DevilaN .
Note that the size of the buffer needs to be at least 1 more than the "%s" width.

#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);

3rd, use fgets() (my preference), Of course this reads a line and not a sequence of non-white-space characters. This often does meet that higher level goal though.

#define MAX_NAME_LEN 256
char name[MAX_NAME_LEN + 2];
fgets(name, sizeof name, input);

You will struggle with fscanf. I don't know better solution than proposed by you if you are bound with fscanf.

Personally I would switch to fgets like in this simple example:

#define MAX_NAME_LEN 25

int main()
{
        char string[MAX_NAME_LEN];
        fgets(string,MAX_NAME_LEN,stdin);

        printf("%s", string);

}

As a reference, following is a fscanf_s() approach to prevent overflow as used by earlier version of VS that did not attain C99 compliance. An additional unsigned argument follows name[] indicator the size of the buffer. This appears to apply to VS 2015 too.

char name[MAX_NAME_LEN + 1];
fscanf_s(input, "%s", name, (unsigned) sizeof name);

C11 includes fscanf_s() in its Annex K which is normative , for information only, so a compliant compiler need not implement it. Yet in C11, it is specified as needing a rsize_t/size_t argument .

fscanf_s(input, "%s", name, sizeof name);

If input is more than 256 non-white-space characters, a matching failure occurs. In this case, fscanf() return EOF . I think the VS version does the same. I think both will consume all the non-white-space in the stream, even if there is not enough room to store them all.


Either of these is a bit different than the below in that extra text is not read into name[] , so no buffer overflow. Any extra text remains for the next input function call. fscanf() returns 1.

fscanf(input, "%256s", name);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM