在C中动态指定scanf的最大字符串长度(如printf中的“%* s”) [英] Specifying the maximum string length to scanf dynamically in C (like "%*s" in printf)
问题描述
我可以使用这种技术指定 scanf
的最大字符数来读取缓冲区
:
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[^\n]", 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, "%[^\n]", buffer); /* WHAT NOW? */
}
此代码容易受到缓冲区溢出 as 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]", n, buffer);
我首先想到的是 *
in %* [* ^ \ n]
表示最大字符串大小传递参数(在本例中为 n
)。这是 *
中 printf
的含义。
My first thought was that the *
in "%*[*^\n]"
meant that the maximum string size is passed an argument (in this case n
). This is the meaning of the *
in printf
.
当我检查 scanf
的文档时,我发现这意味着 scanf
应该丢弃<$ c的结果$ c> [^ \ n] 。
When I checked the documentation for scanf
I found out that it means that scanf
should discard the result of [^\n]
.
这让我有些失望,因为我认为这将是一个非常有用的功能能够为 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?
推荐答案
基本答案
printf()
格式说明符 *
中没有类似的 scanf()
。
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);
}
这强调格式规范中的大小比规模小1缓冲区(它是可以存储而不计算终止空值的非空字符数)。请注意,这与 fgets()
形成对比,其中大小( int
,顺便提一下;不是 size_t
)是缓冲区的大小,而不是一个。有多种方法可以改进功能,但它说明了这一点。 (您可以使用 [^ \ n]
格式替换 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 [^\n]
if that's what you want.)
另外,TimČas中提到评论,如果你想要(其余的)一行输入,你通常最好使用 fgets()
来读取该行,但请记住它包括输出中的换行符(而%63 [^ \ n]
会使换行符被下一个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[^\n]
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.
此外,TR 24731-1'安全'功能由Microsoft(或多或少)实施并在附件K中标准化ISO / IEC 9899-2011(C11标准),明确要求长度:
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("%[^\n]", 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[^\n]", 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屋!