来自 stdin 的 fgets 具有不可预测的输入大小
fgets from stdin with unpredictable input size
我正在尝试从 stdin
中读取一行,但我不知道如何正确处理输入大小至少等于限制的情况。示例代码:
void myfun() {
char buf[5];
char somethingElse;
printf("\nInsert string (max 4 characters): ");
fgets (buf, 5, stdin);
...
printf("\nInsert char: ");
somethingElse = getchar();
}
现在,用户可以做三件事:
- Input less than 4 characters (plus newline): 在这种情况下,stdin 中没有任何内容,随后的
getchar()
正确地等待用户输入;
- 恰好输入 4 个字符(加上换行符):在这种情况下,stdin 中还剩下一个换行符,随后的
getchar()
会读取它;
- 输入超过4个字符(加上换行符):在这种情况下stdin中至少还剩下一个字符,随后的
getchar()
读取它,至少留下 中的换行符
案例 2 和 3 需要使用 while(getchar() != '\n')
之类的东西清空 stdin
,而案例 1 不需要任何额外的操作。据我阅读类似问题的答案和 c-faq 了解到,没有 standard/portable 方法可以知道实际情况是否是 1 中描述的情况。
我搞定了吗?或者实际上 是 一种可移植的方法?或者可能是一种完全不同的方法?
如果有空间,fgets
函数会将换行符存储在缓冲区中。因此,如果字符串中的最后一个字符不是换行符,您就知道需要刷新缓冲区。
fgets (buf, 5, stdin);
if (strrchr(buf, '\n') == NULL) {
// flush buffer
int c;
while ((c = getchar()) != '\n') && (c != EOF));
}
如果假设 空字符 '[=12=]'
永远不会被读取,那么 答案将起作用。
如果读取了 空字符,则 strrchr(buf, '\n')
不会找到任何可能已读取的 '\n'
。
代码可以预先设置缓冲区以查看最后是否读取了 '\n'
。
buf[sizeof buf - 2] = '\n';
if (fgets (buf, sizeof buf, stdin)) {
if (strrchr(buf, '\n') == NULL) {
// incomplete line read. (the usual detection)
} else if (buf[sizeof buf - 2] != '\n') {
// incomplete line read with prior null character (see below note).
}
}
然而,C 标准并未指定在 buf[]
中读取的数据之后的数据保持不变,用某种模式预填充缓冲区不足以检测 空字符 '[=12=]'
已阅读。
is a portable way to do it?
最便携的方法是使用重复调用 fgetc()
或类似的方法而不是 fgets()
。
maybe a totally different approach?
我推荐fgetc()
或者通用但不是C标准的getline()
另一种选择:使用scanf("%4[^\n]%n", buf, &n)
:它非常麻烦,但是一种可移植的方式是可能的。它会跟踪 '\n'
之前读取的字符数,即使有些是 空字符 。
int n = 0;
cnt = scanf("%4[^\n]%n", buf, &n); // Cumbersome to get that 4 here.
// Lots of TBD code now needed:
// Handle if cnt != 1 (\n to be read or EOF condition)
// Handle if n == sizeof buf - 1, (0 or more to read)
// Handle if n < sizeof buf - 1, (done, now get \n)
// A \n may still need to be consumed
// Handle EOF conditions
我正在尝试从 stdin
中读取一行,但我不知道如何正确处理输入大小至少等于限制的情况。示例代码:
void myfun() {
char buf[5];
char somethingElse;
printf("\nInsert string (max 4 characters): ");
fgets (buf, 5, stdin);
...
printf("\nInsert char: ");
somethingElse = getchar();
}
现在,用户可以做三件事:
- Input less than 4 characters (plus newline): 在这种情况下,stdin 中没有任何内容,随后的
getchar()
正确地等待用户输入; - 恰好输入 4 个字符(加上换行符):在这种情况下,stdin 中还剩下一个换行符,随后的
getchar()
会读取它; - 输入超过4个字符(加上换行符):在这种情况下stdin中至少还剩下一个字符,随后的
getchar()
读取它,至少留下 中的换行符
案例 2 和 3 需要使用 while(getchar() != '\n')
之类的东西清空 stdin
,而案例 1 不需要任何额外的操作。据我阅读类似问题的答案和 c-faq 了解到,没有 standard/portable 方法可以知道实际情况是否是 1 中描述的情况。
我搞定了吗?或者实际上 是 一种可移植的方法?或者可能是一种完全不同的方法?
如果有空间,fgets
函数会将换行符存储在缓冲区中。因此,如果字符串中的最后一个字符不是换行符,您就知道需要刷新缓冲区。
fgets (buf, 5, stdin);
if (strrchr(buf, '\n') == NULL) {
// flush buffer
int c;
while ((c = getchar()) != '\n') && (c != EOF));
}
如果假设 空字符 '[=12=]'
永远不会被读取,那么
如果读取了 空字符,则 strrchr(buf, '\n')
不会找到任何可能已读取的 '\n'
。
代码可以预先设置缓冲区以查看最后是否读取了 '\n'
。
buf[sizeof buf - 2] = '\n';
if (fgets (buf, sizeof buf, stdin)) {
if (strrchr(buf, '\n') == NULL) {
// incomplete line read. (the usual detection)
} else if (buf[sizeof buf - 2] != '\n') {
// incomplete line read with prior null character (see below note).
}
}
然而,C 标准并未指定在 buf[]
中读取的数据之后的数据保持不变,用某种模式预填充缓冲区不足以检测 空字符 '[=12=]'
已阅读。
is a portable way to do it?
最便携的方法是使用重复调用 fgetc()
或类似的方法而不是 fgets()
。
maybe a totally different approach?
我推荐fgetc()
或者通用但不是C标准的getline()
另一种选择:使用scanf("%4[^\n]%n", buf, &n)
:它非常麻烦,但是一种可移植的方式是可能的。它会跟踪 '\n'
之前读取的字符数,即使有些是 空字符 。
int n = 0;
cnt = scanf("%4[^\n]%n", buf, &n); // Cumbersome to get that 4 here.
// Lots of TBD code now needed:
// Handle if cnt != 1 (\n to be read or EOF condition)
// Handle if n == sizeof buf - 1, (0 or more to read)
// Handle if n < sizeof buf - 1, (done, now get \n)
// A \n may still need to be consumed
// Handle EOF conditions