繁体   English   中英

在 C 中动态指定 scanf 的最大字符串长度(如 printf 中的“%*s”)

[英]Specifying the maximum string length to scanf dynamically in C (like "%*s" in printf)

我可以使用此技术指定scanf读取到buffer的最大字符数:

char buffer[64];

/* Read one line of text to buffer. */
scanf("%63[^\n]", buffer);

但是如果我们在编写代码时不知道缓冲区长度怎么办? 如果它是函数的参数呢?

void function(FILE *file, size_t n, char buffer[n])
{
    /* ... */
    fscanf(file, "%[^\n]", buffer); /* WHAT NOW? */
}

此代码容易受到缓冲区溢出的影响,因为fscanf不知道缓冲区有多大。

记得之前看到过这个,开始认为是解决问题的方法:

fscanf(file, "%*[^\n]", n, buffer);

我首先想到的是, *"%*[*^\\n]"指的是最大字符串大小传递参数(在这种情况下n )。 这就是printf *的含义。

当我检查scanf的文档时,我发现这意味着scanf应该丢弃[^\\n]

这让我有些失望,因为我认为能够为scanf动态传递缓冲区大小将是一个非常有用的功能。

有什么方法可以将缓冲区大小动态传递给scanf吗?

基本答案

scanf()没有printf()格式说明符*的模拟。

The Practice of Programming 中,Kernighan 和 Pike 推荐使用snprintf()创建格式字符串:

size_t sz = 64;
char format[32];
snprintf(format, sizeof(format), "%%%zus", sz);
if (scanf(format, buffer) != 1) { …oops… }

额外的信息

将示例升级为完整功能:

int read_name(FILE *fp, char *buffer, size_t bufsiz)
{
    char format[16];
    snprintf(format, sizeof(format), "%%%zus", bufsiz - 1);
    return fscanf(fp, format, buffer);
}

这强调了格式规范中的大小比缓冲区的大小小一(它是可以存储的非空字符数,不计算终止空字符)。 请注意,这与fgets()形成对比,其中大小(一个int ,顺便说一句;不是size_t )是缓冲区的大小,而不是少一个。 有多种改进功能的方法,但它表明了这一点。 (如果这是你想要的,你可以用[^\\n]替换格式中的s 。)

此外,正如Tim Čas评论中指出的那样,如果您想要(其余的)一行输入,通常最好使用fgets()来读取该行,但请记住,它在其输出中包含换行符(而%63[^\\n]将换行符留给下一个 I/O 操作读取)。 对于更一般的扫描(例如,2 或 3 个字符串),这种技术可能更好——特别是如果与fgets()getline() ,然后使用sscanf()来解析输入。

此外,由 Microsoft(或多或少)实施并在 ISO/IEC 9899-2011(C11 标准)的附件 K 中标准化的 TR 24731-1“安全”功能明确要求长度:

if (scanf_s("%[^\n]", buffer, sizeof(buffer)) != 1)
    ...oops...

这避免了缓冲区溢出,但如果输入太长可能会产生错误。 可以/应该像以前一样在格式字符串中指定大小:

if (scanf_s("%63[^\n]", buffer, sizeof(buffer)) != 1)
    ...oops...

if (scanf_s(format, buffer, sizeof(buffer)) != 1)
    ...oops...

请注意,对于使用生成的格式字符串的代码,必须忽略或抑制有关“非常量格式字符串”的警告(来自某些编译器的某些标志集)。

scanf系列函数中确实没有可变宽度说明符。 替代方法包括动态创建格式字符串(尽管如果宽度是编译时常量,这似乎有点愚蠢)或简单地接受幻数。 一种可能性是使用预处理器宏来指定缓冲区和格式字符串宽度:

#define STR_VALUE(x) STR(x)
#define STR(x) #x

#define MAX_LEN 63

char buffer[MAX_LEN + 1];
fscanf(file, "%" STR_VALUE(MAX_LEN) "[^\n]", buffer);

另一种选择是#define字符串的长度:

#define STRING_MAX_LENGTH "%10s"

或者

#define DOUBLE_LENGTH "%5f"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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