今天, 我们继续来排错.
从键盘输入整数, 这是很常见的输入方式, 也是容易出错的地方.
我们看一下案例:
#include#define NUM 6 int main(int argc, char** argv) { int n = 0; int i = 0; double sum = 0; printf("Input %d integers:n", NUM); for (; i < NUM; ++i) { scanf("%d", &n); sum += n; } printf("Ave= %.3Lfn", sum / NUM); return scanf("%*s"); }
这段程序的功能, 是输入 6 个整数, 计算其平均数, 我们正常输入, 不会出错;
但是, 如果输入错误的内容 (例如 "A", "3.14"), 它们不能作为整数, 则会得到错误的结果;
并且, 中间一个错误内容, 会影响后续所有输入.
这就需要我们, 利用 scanf 函数的返回值, 来判断输入的合法性.
scanf 返回一个 int 值, 它表示成功读入变量的参数个数;
例如, scanf("%d", &n) 在读取 "23" 时, 成功读取, 返回 1; 而读取 "A" 时, 则返回 0;
当读取失败时, 错误的内容, 会停留在键盘缓冲区;
我们必须清除它们, 否则它会被下一次 scanf 读取到, 从而继续导致错误.
这就好比, 水管中间被异物堵住了, 必须清除异物, 才能让水流恢复正常.
我们使用 scanf("%*s") 可以扫描一个字符串, 但不存入变量中, 遇到 Enter, 空格或 Tab 为止;
这种方法, 可以去除最近一次错误的输入, 从而保证后续输入正常进行:
if (1 > scanf("%d", &n)) { scanf("%*s"); }
注意, scanf("%*s") 永远返回 0, 因为它没有读入任何内容;
我习惯将 main 函数结尾的 "return 0;" 写成 "return scanf("%*s");",
这样可以, 在程序运行结束前, 等待我们输入, 从而让运行结果停留在屏幕中;
否则, 我们可能会遇到, 程序运行的界面一闪而过, 没来得及看结果的情况.
如果我们想用 cin, 就不能这么简单了.
cin, cout, 属于运算符, 而 scanf 和 printf 属于函数, 它们的原理并不相同;
cin 也有返回值, 当它成功读取时, 返回非 0 值, 否则返回 0;
下方的代码, 可以连续读取任意个整数, 同时进行求和, 直到输入不能解读为整数的内容为止:
while (cin >> n) { sum += n; }
注意, 我们最好不要混合使用 scanf 和 cin, 大家试一下就知道了, 容易出现错误.
相对于 scanf 而言, cin *** 作起来不是很方便;
如果你非要用 cin, 最好是自定义一个函数, 来读取字符串中的整数:
int ScanInt(char* str, int& num) { num = 0; if (nullptr == str) { return -1; } bool neg = false; int i = 0; char ch = str[0]; if ('-' == ch) { neg = true; ++i; } for (; '' != (ch = str[i]); ++i) { if (ch < '0' || ch > '9') { num = 0; return -1; } num = ch - '0' + 10 * num; } if (neg) { num = -num; } return 0; }
第 1 个参数 str, 是我们输入的内容;
第 2 个参数 num, 是存放结果的变量, 注意, 它是引用的形式.
返回值为 0, 表示成功读取, 返回 -1 则表示输入错误.
我们以字符串的形式读取数据, 这样的好处是, 遇到错误输入后, 不需要清空缓冲区.
主函数如下:
#includeusing namespace std; #include "Smile.h" #define NUM 6 int main(int argc, char** argv) { char str[32] = ""; int n = 0; int i = 0; Scanner* scn = new Scanner; for (; i < NUM; ++i) { cin >> str; if (scn->ScanInt(str, n)) { cout << "Wrong input!n"; } else { cout << n << endl; } } delete scn; cin >> str; return 0; }
运行结果如下:
类似地, 我们可以再定义一个 ScanDouble, 读取字符串中的浮点数,
然后封装成一个类:
class Scanner { public: // Read a double float number from str; int ScanDouble(char* str, double& res) { res = 0; if (nullptr == str) { return -1; } bool neg = false; int i = 0; char ch = str[0]; if ('-' == ch) { neg = true; ++i; } for (; '' != (ch = str[i]); ++i) { if ('.' == ch) { break; } if (ch < '0' || ch > '9') { res = 0; return -1; } res = (double)(ch - '0') + 10 * res; } if ('' == ch) { return 0; } double pos = 1; while ('' != (ch = str[++i])) { if (ch < '0' || ch > '9') { res = 0; return -1; } pos *= 0.1; res += (double)(ch - '0') * pos; } if (neg) { res = -res; } return 0; } // Read an int number from str; int ScanInt(char* str, int& num) { num = 0; if (nullptr == str) { return -1; } bool neg = false; int i = 0; char ch = str[0]; if ('-' == ch) { neg = true; ++i; } for (; '' != (ch = str[i]); ++i) { if (ch < '0' || ch > '9') { num = 0; return -1; } num = ch - '0' + 10 * num; } if (neg) { num = -num; } return 0; } };
不过, 我还是建议, 大家使用 scanf 和 printf, 尽量不要用 cin 和 cout.
这 2 个函数可以优化, 比如说, 可以一次性读入多个数据.
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)