如何使用#defined 常量作为 fscanf 中的最大字段宽度?

How do I use a #defined constant as the max field width in 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);

确实有效,但是当所有人都出去的时候很笨拙。有没有更优雅的解决方案?

你可以使用这个宏:

#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 扩展到 256INDIRECT(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);

}

第一次简化。

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

第二次提供或更好,使用 stringify
请注意,缓冲区的大小至少需要比 "%s" 宽度大 1。

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

3,使用fgets()(我的偏好),当然这会读取而不是一系列非白色-space字符。不过,这通常确实可以实现更高级别的目标。

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

作为参考,以下是 fscanf_s() 未达到 C99 合规性的 VS 早期版本所使用的防止溢出的方法。 unsigned 参数跟在 name[] 指示缓冲区大小之后。这似乎也适用于 VS 2015。

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

C11 在其附件 K 中包含 fscanf_s(),它是 规范的 ,仅供参考,因此兼容的编译器不需要实现它。然而在 C11 中,它被指定为需要一个 rsize_t/size_t 参数 .

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

如果输入超过256个非白色-space字符,匹配失败。在这种情况下,fscanf() return EOF。我认为 VS 版本也是如此。我认为两者都会消耗流中的所有非白色-space,即使没有足够的空间来存储它们。


其中任何一个都与下面的有点不同,因为额外的文本不会读入 name[],所以没有缓冲区溢出。任何额外的文本都保留用于下一个输入函数调用。 fscanf() returns 1.

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