许多人说scanf
不应该用于更严肃的程序",与getline
相同。
我开始迷失方向:如果我遇到的每个输入功能都表示我不应该使用其中任何一个,那么我应该使用什么?是否有更多的标准"获得我不知道的输入的方式?
答案 0 :(得分:35)
通常,fgets()
被认为是一个不错的选择。它将整行读入缓冲区,从那里你可以做你需要的。如果您想要scanf()
之类的行为,则可以将您阅读的字符串传递给sscanf()
。
这样做的主要优点是,如果字符串无法转换,则很容易恢复,而对于scanf()
,您需要在stdin
上留下需要消耗的输入。另外,你不会把与scanf()
混合的面向行的输入陷入困境,这会导致\n
之类的事情在stdin
上留下令人头疼的问题,这通常会导致新的编码人员相信输入调用完全被忽略了。
这样的事可能是你喜欢的:
char line[256];
int i;
if (fgets(line, sizeof(line), stdin)) {
if (1 == sscanf(line, "%d", &i)) {
/* i can be safely used */
}
}
您应该注意fgets()
在EOF或错误上返回NULL
,这就是我将其包裹在if
中的原因。 sscanf()
调用返回已成功转换的字段数。
请记住,如果该行大于您的缓冲区,则fgets()
可能无法读取整行,而在“严肃”程序中肯定是您应该考虑的。
答案 1 :(得分:11)
对于可以在输入长度上设置固定限制的简单输入,我建议您使用fgets()
从终端读取数据。
这是因为fgets()
允许你指定缓冲区大小(而不是gets()
,因为这个原因应该从不用于读取人类的输入):
char line[256];
if(fgets(line, sizeof line, stdin) != NULL)
{
/* Now inspect and further parse the string in line. */
}
请记住它会保留,例如换行符可能会令人惊讶。
更新:正如评论中所指出的,如果您对跟踪内存负责,可以选择更好的替代方案:getline()
。这可能是POSIX代码的最佳通用解决方案,因为它对要读取的行的长度没有任何静态限制。
答案 2 :(得分:5)
使用scanf
时存在几个问题:
使用普通%s
转化说明符阅读文字与使用gets()
具有相同的风险;如果用户输入的字符串长度超过目标缓冲区的大小,则会出现缓冲区溢出;
如果使用%d
或%f
来读取数字输入,则无法捕获并完全拒绝某些错误模式 - 如果您正在读取带%d
的整数并且用户类型“12r4
”,scanf
将转换并分配12
,同时在输入流中留下r4
以填充下一个读取内容;
某些转化说明符会跳过前导空格,而其他转换说明符则不会,并且未将其考虑在内会导致出现完全跳过某些输入的问题;
基本上,使用scanf
进行防弹读取需要额外的批次。
一个不错的选择是使用fgets()
将所有输入作为文本读取,然后使用sscanf
或strtok
,strtol
,{{的组合来对输入进行标记和转换1}}等等。
答案 3 :(得分:2)
使用fgets
获取数据并使用sscanf
(或其他方法)来解释数据。
请参阅此页,了解为何最好使用fgets
+ sscanf
而不是scanf