我应该使用fgets或scanf有限的输入在c?

0

的问题

我应该使用 fgets 或者格式化 scanf 喜欢 scanf("%10s", foo).

除外, scanf 不读的空白人物,这是可以解决,并做更多的东西与scanset,那么为什么我应该使用 fgets 而不是的 scanf?

任何帮助,将不胜感激。


编辑

有一件事我想问的是:即使当我们使用 fgets 什么发生,如果用户进入字超过边界(我的意思是一个很大的角色),它导致缓冲区溢? 然后怎样处理它?

c fgets input scanf
2021-11-23 13:53:00
4

最好的答案

5

在大多数的操作系统用户输入的,默认的,行-基础。 为此原因之一是允许用户按backspace键正确的输入之前,发送的输入的程序。

用于基于行的用户输入的,这是有意义和直观的一种方案来读一行输入的时间。 这是什么样的功能 fgets 会(提供足够大的缓冲储存的整个行输入)。

功能 scanf另一方面,一般不读一行输入的时间。 例如,当你使用 %s%d 转换格式说明与 scanf它不会耗费一整行的输入。 相反,它只会消耗大的输入作为比赛的转换格式的说明符。 这意味着newline character结束时行通常不会被消耗(它可以很容易地导致编程bug). 此外, scanf 所谓的 %d 转换格式说明将考虑诸如输入 6sldf23dsfh2 为有效输入数 6但任何进一步的话 scanf 与同一说明将会失败,除非你放弃其余的线输入流。

这种行为的 scanf 是反直觉的,而该行为 fgets 是直观,在处理线的用户输入。

在使用之后 fgets你可以使用的功能 sscanf 上弦,用于分析的内容的个别行。 这会让你继续使用scansets. 或者你可以分析该线通过其他一些手段。 无论哪种方式,只要你使用 fgets 而不是的 scanf 对于阅读的输入,必将处理的一线的输入时间,这是天然的和直观的方式处理基于行的用户输入。

当我们使用 fgets 什么发生,如果用户进入字超过边界(我的意思是一个很大的角色),它导致缓冲区溢? 然后怎样处理它?

如果用户输入更多的人物适合于在缓冲区指定第二 fgets 功能的论点,那么它不会溢出的缓冲区。 相反,它只会提取尽可能多的字符从输入流的配合缓冲区。 你可以确定是否在整个行读通过检查是否符串中包含一个newline character '\n' 在结束。

2021-11-23 15:13:39

太谢谢你了,你回答的真正帮助我。
Becker

该行为的fgets()只是直观的投入,是不是比预期更长的时间。
supercat
2

这是一个常讨论的主题,充分的意见,但有趣的。 我已经观察到,大多数是那些已经答复了类似的问题在这个网站上落在侧 fgets(). 我是他们中的一个。 我找到 fgets() 要更好地使用用户输入比 scanf() 有几个例外情况。 scanf() 被许多人视为作为 次优的方法处理用户输入的. 例如

"...它会告诉你它是否成功或失败,但可以告诉你只有大约在那里失败,并不是所有的如何或为什么。 你的 很少有机会做任何错误的恢复。"
(jamesdlin). 但是,在感兴趣的尝试平衡,将开始关闭的理由是 这种讨论.

用户输入来自 stdin即键盘输入, fgets() 将是一个更好的选择。 它是更加宽容的在于 字符串的 它会读取可以充分验证 之前 转换尝试

为数不多的几次之一,使用一种形式的 scanf(): 的fscanf() 会好起来的使用可能会转换时,输入从一个非常控制来源,即来自读一严格的格式的文件,与重复的可预测的领域。

更多的讨论中, 这种比较 的两个突出了另外的优点和缺点。

编辑:解决作附加的问题有关的溢出:

"有一件事我想问的是:即使当我们使用fgets什么发生,如果用户进入字超过边界(我意味着很多 字符),它导致缓冲区溢? 然后怎么处理 它?"

[fgets()](https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm 是很好的设计以防止缓冲区溢,只是通过使用其参数适当,例如:

char buffer[100] = {0};
...
while fgets(buffer, sizeof buffer, stdin);  

这可以防止输入大于缓冲区的大小从正在被处理,因此防止溢出。

甚至使用 scanf()防止缓冲区溢出是相当直截了当:使用一个 宽说明 在格式串。 如果你想读输入,例如和限制输入的大小从用户100个最大,该守则将包括下列:

char buffer[101] = {0};// includes space for 100 + 1 for NULL termination

scanf("%100s", buffer);
        ^^^  width specifier 

然而,溢出是不是很好用 scanf(). 来证明,使用这种简单的代码输入两个值表示的评论每个运行:

int main(void)
{
    int val = 0;
    // test with 2147483647 & 2147483648
    scanf("%d", &val);
    printf("%d\n", val);
    
    return 0;
}

第二值,我的系统发送以下:

NON-FATAL RUN-TIME ERROR: "test.c", line 11, col 5, thread id 22832: Function scanf: (errno == 34 [0x22]). Range error `

在这里你需要读一个字符串中,然后跟着一串号码转换,使用的一个 strto_() 职能: strtol(), strtod(),...). 两者都包括能力测试的溢出 之前 造成一个运行时的警告或误差。 注意使用 atoi(), atod() 不会保护从溢出。

2021-11-23 14:10:20

谢谢你,我真的很感激你的答案。
Becker

一个问题是"满意"? 我必须恭敬地不同意。 这不是一个问题的意见, scanf 几乎是完全没用的,好的最好的阅读单一的、简单的投入在介绍对C节目,但非常难以做任何事情远程复杂--这些都是显而易见的事实! :-)
Steve Summit
1

迄今为止,所有的答案在这里提出的错综复杂的 scanffgets但我认为值得一提的是,这些职能已过时,在当前的C的标准。 Scanf是特别危险,因为它有各种各样的安全问题与缓冲区溢出。 fgets 是不是有问题的,但是从我的经验,它往往是有点笨重的和不那么有用的做法。

事实是,通常你真的不知道输入的用户会。 你可以绕过这个通过使用fgets与 我希望这将足够大的 缓冲,但是这不是真的ellegant. 相反,什么,你常常想要做的就是动态的缓冲区,将成长到足够大,以储存的任何用户输入将会提供。 这是当 getline 功能发挥作用。 它是用来 读取的任何数字从用户,直至\n遇到. 从本质上讲,它载荷整个线以你的记忆,作为一串。

size_t getline(char **lineptr, size_t *n, FILE *stream);

这个函数 的一个指向一个动态分配串 作为第一个参数, 并指尺寸的分配缓冲 作为第二个参数和 作为第三个论点。 (你基本上将地方 stdin 有的命令行输入)。 和返回的数量读取的字符,包括\n结束时,但不终止null。

在这里,你可以看到的例子使用的这个功能:

int main() {

printf("Input Something:\n");  // asking user for input

size_t length = 10;                   // creating "base" size of our buffer
char *input_string = malloc(length);  // allocating memory based on our initial buffer size
size_t length_read = getline(&input_string, &length, stdin);  // loading line from console to input_string
// now length_read contains how much characters we read
// and length contains new size of our buffer (if it changed during the getline execution)

printf("Characters read (including end of line but not null at the end)"
       ": %lu, current size of allocated buffer: %lu string: %s"
       , length_read, length, input_string);

free(input_string);    // like any other dynamically-allocated pointer, you must free it after usage
return 0;
}

当然,使用该功能需要的基本知识有关的指针和动态内存在C,但是稍微更加复杂的性质 getline 肯定是值得的,因为提供安全和灵活性。

你可以读取更多有关这个功能,以及其他的输入功能在C,在这个网站: https://www.studymite.com/blog/strings-in-c 我认为,它总结了错综复杂的C输入得相当好。

2021-11-23 19:18:00

谢谢你的建议和链接,它可以帮助我很多。
Becker
1

如果你有例如一个字阵列宣布喜欢

char s[100];

并想要读一个字符串,其中包含嵌入式的空间,然后你可以使用 scanf 以下的方式:

scanf( "%99[^\n]", s );

fgets 如:

fgets( s, sizeof( s ), stdin );

这两者之间的区别的呼吁是呼叫的 scanf 不能读取新的行符 '\n' 从输入缓冲区。 同时 fgets 读取新的行符 '\n' 如果有足够的空间字阵列。

删除新的线性格 '\n' 这是存在的字阵列,使用后 fgets 你可以写例如:

s[ strcspn( s, "\n" ) ] = '\0';

如果输入串已超过99个字然后这两个电话只读99字和追加的字符序列的终止零字 '\0'. 所有剩余的字符将仍然在输入缓冲区。

有一个问题 fgets. 例如,如果之前 fgets 有用 scanf 例如:

scanf( "%d", &x );
fgets( s, sizeof( s ), stdin );

并且用户输入是:

10
Hello World

然后叫的 fgets 将只读新的线性格 '\n' 这就是存在缓冲区之后按下输入键时整数值在呼叫的 scanf 是阅读。

在这种情况下你需要编写代码,将删除新的线性格 '\n' 之前的呼叫 fgets.

你可以这样做,例如以下方式:

scanf( "%d", &x );
scanf( " " );
fgets( s, sizeof( s ), stdin );

如果您使用 scanf 然后在这种情况下你可以写:

scanf( "%d", &x );
scanf( " %99[^\n]", s );
       ^^ 
2021-11-23 14:05:15

其他语言

此页面有其他语言版本

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................