正如@5gon12eder所建议的,使用:
char user[16];
fgets(user, sizeof user, stdin);
#include <stdio.h>
char *fgets(char * restrict s, int n, FILE * restrict stream);
现在进入细节:
'\n' 和 '\0' 不会自动添加,只有 '\0' 会自动添加。fgets() 函数在读取到 '\n' 时停止读取,但也可能因为缓冲区已满等其他原因而停止读取,在这些情况下,'\n' 不存在于 '\0' 前面。
fgets() 不是读取 C 字符串,而是读取一行文本,输入流通常处于文本模式,进行换行转换。在某些系统上,'\r\n' 将被翻译成 '\n',而在其他系统上则不会。通常情况下,文件读取匹配这种转换,但也会有例外。在二进制模式下,没有任何转换发生。
fgets() 会读入 '\0',然后继续读取。因此使用 strlen(buf) 并不能总是反映读取的字符数。虽然可以有一种确定中间存在 '\0' 时读取真实字符数的方法,但使用 fread() 或 fgetc() 更容易编写代码。
当 EOF 条件(且未读取数据)或 I/O 错误时,fgets() 返回 NULL。当发生 I/O 错误时,缓冲区的内容未定义。
C 标准使用 int 类型作为缓冲区大小,但通常代码传递 size_t 类型的变量。小于 1 或大于 INT_MAX 的大小可能会出现问题。大小为 1 应该仅填充 buf[0] = '\0',但某些系统行为不同,特别是在接近或超过 EOF 条件时。但只要 2 <= n <= INT_MAX,则可以期望有终止的 '\0'。注意:当 size 太小时,fgets() 可能会返回 NULL。
代码通常希望删除终止符 '\n',但使用某些代码可能会导致问题。建议使用以下代码:
```c
char buf[80];
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_IOError_or_EOF();
// IMO potential UB and undesired behavior
// buf[strlen(buf)-1] = '\0';
// Suggested end-of-line deleter
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') buf[--len] = '\0';
```
健壮代码检查 fgets() 的返回值。下面的方法存在缺陷:
1. 如果发生 I/O 错误,缓冲区内容未定义。检查缓冲区内容将无法提供可靠的结果。
2. '\0' 可能是读取的第一个字符,并且文件不在 EOF 条件下。
```c
// Following is weak code.
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
if (strlen(buf) == 0) Handle_EOF();
// Robust, but too much for code snippets
if (fgets(buf, sizeof buf, stdin) == NULL) {
if (ferror(stdin)) Handle_IOError();
else if (feof(stdin)) Handle_EOF();
else if (sizeof buf <= 1) Handle_too_small_buffer(); // pedantic check
else Hmmmmmmm();
}
```
\n
将保留在流中。它会在下一次读取时出现。 - Fred Larsonchar user[16];
。 - 5gon12ederprintf
并不保证会导致段错误。 - mafso