数据输入与输出
一个标准的 C++ 程序,基本上都会涉及到数据的输入和输出,可以把我们要写的程序想像成一个黑盒子,一端输入数据,经过程序的各种魔法处理,再输出处理后的数据。
前面的程序中,我们已经有使用 cin
来读入数据,使用 cout
来输出数据,而且我们也知道了它们是属于标准输入输出流中的一部分。
标准输入输出
cin
与 cout
要使用 cin
与 cout
,需要引入 iostream
头文件。iostream
是 input / output stream 的缩写,它是 C++ 中用于数据输入与输出的标准头文件,属于 C++ 标准函数库的一部分。
#include <iostream>using namespace std;
int main() { int a, b, c; cin >> a >> b >> c; cout << "三数之和为 " << a + b + c << endl;}
endl
代表 end line,简单说就是换行,也可以换成 \n
,是同样的效果。
按行读入字符串
假设有下面一段代码去读入字符串:
#include <iostream>using namespace std;
int main() { string s; cin >> s;
cout << s << endl; return 0;}
输入数据
hello world!
运行程序,得到
hello
可以发现,后面的 world!
没有读进来,这是因为在默认情况下,无论使用 cin
还是 scanf()
来读入数据,在遇到空格或换行后都会停止读入。
如果要读入完整一行内容(包括空格),可以使用getline()
。
#include <iostream>using namespace std;
int main() { string s; getline(cin, s); // 按行读入(包括当前行的所有内容)
cout << s << endl; return 0;}
运行程序,输出结果正确。
hello world!
空行读入的问题
偶尔我们还会同时使用 cin
和 getline()
去读入数据,这里就很容易遇到读入空行的问题。
比如说,我们希望读入下面这样的数据,一行是年龄,一行是姓名:
18Jordan Chan
我们很容易写出下面这段代码:
#include <iostream>using namespace std;
int main() { int age; cin >> age;
string name; getline(cin, name);
cout << age << endl; cout << name << endl; return 0;}
运行程序,与期待的结果不一样,发现第二行字符串 Jordan Chan
没有读入进来。
这是因为,当我们使用 cin
读完 18 就停下来了(停在 18 与换行符之间),当使用 getline()
读入时,会首先读入 18 后面的换行符,然后就停下来,因此多读入了一个空行,反而真正想读的下一行数据没有读入进来。
解决办法,在 cin
后添加语句 cin.ignore()
,忽略掉 cin
后面的换行符。
#include <iostream>using namespace std;
int main() { int age; cin >> age; cin.ignore(); // 忽略 cin 后面的换行符
string name; getline(cin, name);
cout << age << endl; cout << name << endl; return 0;}
scanf()
与printf()
要使用scanf()
与printf()
,需要引入 cstdio
头文件。cstdio
是将 C 中的 stdio.h
内容以 C++ 头文件的形式表示出来的写法,stdio.h
是 C 标准函数库中的头文件,是 standard buffered input & ouput 的缩写。
下面的代码我们使用了 scanf()
来读入数据,使用 printf()
来输出数据。
#include <cstdio>using namespace std;
int main() { int a, b, c; scanf("%d %d %d", &a, &b, &c);
printf("三数之和为 %d\n", a + b + c);}
这里双引号中的百分号 %
,代表后面紧跟的是格式化指令,不同的变量类型,对应不同的格式指令,下面是几组常见的格式化类型指令。
%d
,这里的d
代表 decimal,表示以整数输出%f
,这里的f
代表 float,表示以浮点数输出%lf
,这里的lf
代表 double,表示以双精度浮点数输出%s
,这里的s
代表 string,表示以字符串输出%c
,这里的c
代表 character,表示以字符输出
格式化指令当然不止这些,记住一些常用的就行了,在实际使用中,那些不常见的指令可以去查阅相应的文档。
下面两种情况,建议使用 scanf() / printf()
:
- 一些带复杂的格式化输出,使用
printf()
更为方便 - 需要读取的数据很大时,使用
scanf()
读入,效率会更高一些
一般来说,在使用
cin
或cout
的地方,一定可以替换为scanf
或printf
,反过来就不行!
格式化输出
当我们在输入数据时,很多时候都会涉及到结果的格式化输出。
来看一个简单的例子。
#include <iostream>using namespace std;
int main() { float a, b; cin >> a >> b;
cout << a / b << endl;
return 0;}
下面是几组输入输出数据。
输入数据 1
2 3
输出数据 1
0.666667
输入数据 2
20 3
输出数据 2
6.66667
输入数据 3
200 3
输出数据 3
66.6667
如果将程序改为 scanf
和 printf
输入输出,代码如下所示:
#include <iostream>using namespace std;
int main() { float a, b; scanf("%f %f", &a, &b);
printf("%f", a / b);
return 0;}
同样的上面三组输入数据,结果如下。
输入数据 1
2 3
输出数据 1
0.666667
输入数据 2
20 3
输出数据 2
6.666667
输入数据 3
200 3
输出数据 3
66.666664
可以发现它们之间的区别。
- 当我们用
cout
输出时,结果默认保留 6 位数字(除开小数点后的所有数字) - 当我们用
printf
输出时,结果默认保留小数点后 6 位数字
在实际应用中,我们如何去控制输出数据的格式呢,比如,如果想结果保留小数点后两位,该怎么做?
cout
进行格式化输出
#include <iostream>#include <iomanip>using namespace std;
int main() { const float PI = 3.1415926; // 定义为常量
cout << fixed << setprecision(2) << PI << endl;
return 0;}
这里有两点需要注意:
- 要进行格式化输出,需要引入
<iomanip>
头文件,或者使用万能头文件 setprecision
用于设置精度,但精度所代表的具体含义是由fixed
来确定的,加上fixed
,表示保留小数点后两位,如果没有fixed
,表示除开小数点后保留两位数。
printf
格式化输出
用 printf()
实现同样的保留小数点后两位输出,代码如下:
#include <iostream>#include <iomanip>using namespace std;
int main() { const float PI = 3.1415926; // 定义为常量
printf("%.2f", PI);
return 0;}
这里的 .2f
表示结果以浮点数(float
)输出,并且小数点后保留两位,相比 cout
的格式化输出更加简洁、直观。
更复杂的格式化输出
对于一些比较复杂的格式化输出,使用 printf()
可能会更方便一些。
例如,我们想希望输入两个整数 和 ,按下面的指定格式输出。
输入:
5 3
输出:
a + b = 8, a - b = 2
如果使用 cout
输出,代码如下:
#include <iostream>#include <cstdio>using namespace std;
int main() { int a, b; cin >> a >> b;
cout << "a + b = " << a + b << ", " << "a - b = " << a - b << endl;
return 0;}
使用 printf()
输出,代码如下:
#include <iostream>#include <cstdio>using namespace std;
int main() { int a, b; cin >> a >> b;
printf("a + b = %d, a - b = %d\n", a + b, a - b);
return 0;}
可以直观看到,使用 printf()
的格式化输出同样会更简洁一些。
小结
在本章节中,我们快速梳理了一下 C++ 中与输入输出相关的基本知识点,在实际应用中,关于输入输出的内容还有很多,比如更多的格式化输出、说文件读入与输出等高级主题,对于初学者和刚入门的竞赛选手来说,掌握上面的这些知识点基本上够用了。