C语言3条件while循环实现问题:手机号输入校验函数异常
代码问题排查与优化方案
存在的核心bug
- 循环判断逻辑恒成立,是程序直接跳转到重试提示的根本原因
原代码写的循环条件中strlen(number) != 11 + ' ',是将空格的ASCII码值(32)与11做加法得到43,以此判断字符串长度不等于43。从逻辑运算角度看,不管字符串实际长度是多少,长度不等于11 或者 长度不等于43这个表达式永远为真——不存在能同时满足“长度等于11”和“长度等于43”的字符串,因此整个循环条件只要满足“输入不是空串”就会触发,根本不会判断输入是否合法,自然运行后直接跳转到重试提示,不会等待输入。 - 逻辑混淆:把字符ASCII值和实际字符串长度做等价
带单个空格的有效号码,实际长度是11个数字加1个空格,总长度为12,完全不需要把空格的ASCII值参与长度计算,这部分逻辑完全偏离需求。 - 未适配fgets的读取特性
用fgets读入用户输入时,会把用户按回车产生的换行符\n一并存入字符串末尾。比如输入11位无空格的号码按回车,读到的字符串实际为01234567890\n,计算出的长度是12,和预期的11完全不匹配,长度校验从一开始就会失效。 - 缺少必要的内容校验
既没有判断第一个字符是否为要求的数字0,也没有校验字符串中的空格数量、非空格位置是否全为数字,完全不满足需求的校验规则。 - 输入缓冲区残留问题
fgets会优先读取输入缓冲区中的残留内容,如果之前的操作没有清空缓冲区,fgets会直接读到残留的换行符,根本不会暂停等待用户输入。
优化实现代码
按照需求(以0开头、总计11位数字、允许最多1个空格分隔、其余位置全为数字),采用逐字符校验的方式实现,逻辑清晰且可避免fgets常见坑点:
#include <stdio.h> #include <string.h> #include <ctype.h> void phone() { char number[20]; int is_valid; int space_count; int digit_count; printf("Please enter your phone number: "); fgets(number, sizeof(number), stdin); // 持续重试直到输入合法 while (1) { is_valid = 1; space_count = 0; digit_count = 0; // 移除fgets读入的末尾换行符,修正长度计算偏差 size_t str_len = strlen(number); if (str_len > 0 && number[str_len - 1] == '\n') { number[str_len - 1] = '\0'; str_len--; } // 规则1:输入必须以0开头 if (number[0] != '0') { is_valid = 0; } // 逐字符遍历校验内容合法性 for (int i = 1; i < str_len; i++) { if (number[i] == ' ') { space_count++; // 最多允许1个空格,超过则判定不合法 if (space_count > 1) { is_valid = 0; break; } } else if (isdigit(number[i])) { digit_count++; } else { // 既不是空格也不是数字,判定不合法 is_valid = 0; break; } } // 规则2:去掉开头的0后,剩余数字需凑够10位,总数字数为11位 if (digit_count != 10) { is_valid = 0; } // 全部校验通过则退出循环 if (is_valid) { break; } // 输入不合法,提示重试并清空缓冲区残留内容 printf("please try again\n"); int clear_ch; while ((clear_ch = getchar()) != '\n' && clear_ch != EOF); fgets(number, sizeof(number), stdin); } }
实现说明
- 校验逻辑完全对齐需求:要求号码开头为0、总计11位数字、最多出现1个空格、其余位置必须为数字
- 自动处理fgets读入的换行符,避免长度计算偏差
- 每次重试前清空输入缓冲区残留,从根源上解决fgets不等用户输入、直接读残留的问题
- 逐字符遍历的校验方式比单纯对比字符串长度的鲁棒性更强,可识别非数字的非法输入
内容的提问来源于stack exchange,提问作者ursula。
相关产品推荐
相关产品推荐

