C语言仅用stdio.h时scanf读取int类型如何拦截浮点输入
问题根因
scanf的%d格式匹配整数时,只会从当前位置连续读取符合整数规则的字符,遇到第一个非法字符就停止,不会消费非法字符,也不会报错:
- 第一个参数输入浮点数(例如
4.2 5)时,第一个%d读到4就停止,后续字符从.开始,第二个%d无法匹配整数,scanf返回1,能被现有校验拦截。 - 第二个参数输入浮点数(例如
3 6.7)时,第一个%d正常读入3,第二个%d读到6就停止,剩余的.7留在输入缓冲区,此时scanf成功匹配2个整数、返回值为2,直接绕过了返回值校验,本质是没有校验两个整数读完后是否存在非法的后缀内容。
修复方案(仅使用stdio.h)
根据使用场景不同,可选择以下两种实现方式:
方案1:适用于OJ/文件重定向输入场景
支持两个整数跨多行输入,校验逻辑为:两个整数读取成功后,逐字符跳过输入流中后续所有空白字符,如果跳过所有空白后仍能读到非EOF的字符,说明输入包含非法内容,直接判定失败。
#include <stdio.h> void sum(int a, int b); void dif(int a, int b); void prod(int a, int b); void qut(int a, int b); int main() { int x, z; int read_cnt = scanf("%d %d", &x, &z); if (read_cnt != 2) { printf("n/a\n"); return 2; } // 校验后续是否存在非法非空白字符 int c; while ((c = getchar()) != EOF) { if (c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f' && c != '\v') { printf("n/a\n"); return 2; } } sum(x, z); dif(x, z); prod(x, z); qut(x, z); return 0; } void sum(int a, int b) { printf("%d ", a + b); } void dif(int a, int b) { printf("%d ", a - b); } void prod(int a, int b) { printf("%d ", a * b); } void qut(int a, int b) { // 原逻辑中a为0时0/b为合法运算,仅需拦截除数b为0的场景 if (b == 0) { printf("n/a\n"); } else { printf("%d\n", a / b); } }
注:该方案在交互式终端下运行时,需要用户输入完成后按Ctrl+D(macOS/Linux)或Ctrl+Z(Windows)发送EOF才会执行计算,适合非交互的判定场景。
方案2:适用于交互式命令行场景
要求用户在同一行内输入两个整数,输完按回车即可得到结果,不会阻塞等待后续输入,实现方式为先用fgets读取整行输入,再用sscanf解析内容并校验整行合法性:
#include <stdio.h> void sum(int a, int b); void dif(int a, int b); void prod(int a, int b); void qut(int a, int b); int main() { int x, z, pos; char buf[1024]; // 读取整行输入 if (fgets(buf, sizeof(buf), stdin) == NULL) { printf("n/a\n"); return 2; } // 解析两个整数,同时记录解析完成的位置 int read_cnt = sscanf(buf, "%d %d %n", &x, &z, &pos); if (read_cnt != 2) { printf("n/a\n"); return 2; } // 校验解析位置之后的所有剩余字符是否都是空白 while (buf[pos] != '\0') { char c = buf[pos]; if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { printf("n/a\n"); return 2; } pos++; } sum(x, z); dif(x, z); prod(x, z); qut(x, z); return 0; } void sum(int a, int b) { printf("%d ", a + b); } void dif(int a, int b) { printf("%d ", a - b); } void prod(int a, int b) { printf("%d ", a * b); } void qut(int a, int b) { if (b == 0) { printf("n/a\n"); } else { printf("%d\n", a / b); } }
该方案可以覆盖所有非法场景:空输入、字符串输入、任意位置的浮点数输入、多余字符输入,且交互体验符合普通命令行程序的使用习惯。
内容的提问来源于stack exchange,提问作者v_util
相关产品推荐
相关产品推荐

