在C中使用fgets读取字符串



我正在尝试使用以下代码从用户读取字符串

char array[4];
fgets(array , 5, stdin);

我正在使用fgets命令,因为使用scanf它读取整个字符串,而不管数组的大小,如果它不适合在数组内,它会自动改变数组的大小,以便字符串适合。我想总是读取一个最大长度为4的字符串,这就是为什么我使用fgets,因为无论用户输入的字符串有多长,fgets总是会读取你让它读取的字符。

我的问题是这样的,正如你所看到的,我已经声明了大小为4的数组,但在fgets中我必须写5,因为它比数字少读一个字符。为什么会这样呢?为什么它比数字少读一个字符?我做错了什么吗?

C语言中的字符串是包含终止零字符''的字符序列。

那么这个数组

char array[4];

可以包含一个除了终止零字符''以外最多三个字符的字符串。

如果你想输入三个以上的字符,你需要声明数组至少像

char array[5];

然后写

fgets(array , sizeof( array ), stdin);

在这种情况下,与按下Enter键对应的新行字符'n'将不会存储在数组中,而将留在输入缓冲区中。

因此,如果您在此之后将再次调用fgets,则将读取仅包含新行字符'n'(如果不计算终止零字符'')的字符串。

所以最好像

这样声明数组
char array[6];

要从数组中删除新的行字符,可以写入

array[ strcspn( array, "n" ) ] = '';

对于这个呼叫

fgets(array , 5, stdin);

则如果您尝试输入四个字符,它将调用未定义的行为,因为结束的零字符''将由函数在数组外的内存中写入。

对于函数scanf,你可以这样使用它

char array[5];
scanf( "%4s", array );

char array[5];
scanf( " %4[^n]", array );

注意格式字符串中的前导空格。它允许跳过空白字符,例如新的行字符'n',它可以在之前调用scanf之后存储在缓冲区中。

编写万无一失的输入例程很难。许多传统的*nix程序在遇到不寻常的或极端的输入(例如很长的行)时会失败。

不幸的是,初学者的问题经常处理手动输入。我看到了以下策略,这取决于用例(例如,确切的赋值)。

  1. 忽略所有输入问题,不处理任何错误。假设没有字长于x字节,没有行长于y字节,没有文件大于z字节。不建议这样做,即使您非常确定手动输入不会违反您的任意假设:约束被遗忘,例程被重新定义,并且不可避免地会有一个以这种或那种方式故障的程序。
  2. 一些明智的错误检查。这是传统的*nix程序在黑客攻击成为如此大的问题之前所做的。处理最常见的错误(未找到文件)和最可能导致问题的约束(行长度),并在超出这些限制时失败。您的程序在某些情况下仍然可能失败,并且容易受到攻击。
  3. 让你的程序防傻瓜。这是相对困难的,即使对于简单的程序也是如此,并且往往会模糊实际的控制流和错误检查和处理的目的。另一方面,当你的例程被插入到下一个更大的火箭的软件中时,它将触发一个断言或以其他方式优雅地失败。

对于你的情况,你必须:

  1. 确保例程不会读取超过缓冲区中可用空间的字符,这是Vlad给你的信息。
  2. 想想你如何检测太长的单词或行。
  3. 优雅地处理检测到的条件。

作为题外话,scanf("%5s", buf)也将读取最多5个字符,而不是更多,并在这些字符之后存储一个空字符,因此您的缓冲区数组必须有6个字符大。