在 C 中动态指定 scanf 的最大字符串长度(如 printf 中的“%*s") [英] Specifying the maximum string length to scanf dynamically in C (like "%*s" in printf)

查看:18
本文介绍了在 C 中动态指定 scanf 的最大字符串长度(如 printf 中的“%*s")的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I can specify the maximum amount of characters for scanf to read to a buffer using this technique:

char buffer[64];

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

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

But what if we do not know the buffer length when we write the code? What if it is the parameter of a function?

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

此代码容易发生缓冲区溢出,因为 fscanf 不知道缓冲区有多大.

This code is vulnerable to buffer overflows as fscanf does not know how big the buffer is.

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

I remember seeing this before and started to think that it was the solution to the problem:

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

我的第一个想法是 "%*[*^ ]" 中的 * 意味着将最大字符串大小传递给一个参数(在这种情况下 <代码>n).这就是printf*的含义.

My first thought was that the * in "%*[*^ ]" meant that the maximum string size is passed an argument (in this case n). This is the meaning of the * in printf.

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

When I checked the documentation for scanf I found out that it means that scanf should discard the result of [^ ].

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

This left me somewhat disappointed as I think that it would be a very useful feature to be able to pass the buffer size dynamically for scanf.

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

Is there any way I can pass the buffer size to scanf dynamically?

推荐答案

基本答案

scanf() 中没有类似于 printf() 格式说明符 * 的类似物.

Basic answer

There isn't an analog to the printf() format specifier * in scanf().

编程实践中,Kernighan 和 Pike 推荐使用snprintf() 创建格式字符串:

In The Practice of Programming, Kernighan and Pike recommend using snprintf() to create the format string:

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

额外信息

将示例升级为完整功能:

Extra information

Upgrading the example to a complete function:

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)是缓冲区的大小,一个也不少.有多种改进功能的方法,但它表明了这一点.(如果需要,您可以将格式中的 s 替换为 [^ ].)

This emphasizes that the size in the format specification is one less than the size of the buffer (it is the number of non-null characters that can be stored without counting the terminating null). Note that this is in contrast to fgets() where the size (an int, incidentally; not a size_t) is the size of the buffer, not one less. There are multiple ways of improving the function, but it shows the point. (You can replace the s in the format with [^ ] if that's what you want.)

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

Also, as Tim Čas noted in the comments, if you want (the rest of) a line of input, you're usually better off using fgets() to read the line, but remember that it includes the newline in its output (whereas %63[^ ] leaves the newline to be read by the next I/O operation). For more general scanning (for example, 2 or 3 strings), this technique may be better — especially if used with fgets() or getline() and then sscanf() to parse the input.

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

Also, the TR 24731-1 'safe' functions, implemented by Microsoft (more or less) and standardized in Annex K of ISO/IEC 9899-2011 (the C11 standard), require a length explicitly:

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

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

This avoids buffer overflows, but probably generates an error if the input is too long. The size could/should be specified in the format string as before:

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

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

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

Note that the warning (from some compilers under some sets of flags) about 'non-constant format string' has to be ignored or suppressed for code using the generated format string.

这篇关于在 C 中动态指定 scanf 的最大字符串长度(如 printf 中的“%*s")的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆