第五章 C++ 输入与输出
在 C++ 中,数据的输入与输出是程序与外界交互的唯一桥梁。C++ 同时继承了 C 语言的高效输入输出库 <cstdio>,并提供了面向对象的流输入输出库 <iostream>。本章将详细讲解这两套输入输出系统的使用方法、格式化排版技术以及竞赛中至关重要的效率优化技巧。
5.1 使用标准输入/输出(C 风格)
标准输入输出使用 C 语言传统的高效库。
* 头文件:<cstdio>
* 变量约定:
* 可以使用文件指针 FILE *fin, *fout; 分别代表输入文件和输出文件。
* 在标准的控制台读写中,将它们替换为 stdin 和 stdout,即代表从键盘输入以及输出到屏幕。
1. 格式化输出:printf / fprintf
输出字符串或变量的值可以使用:
printf("格式字符串", 变量1, 变量2, ...);
// 或者输出到指定文件指针 fout 中
fprintf(fout, "格式字符串", 变量1, 变量2, ...);
常用格式占位符:
| 格式字符 | 含义 | 格式字符 | 含义 |
|---|---|---|---|
%d |
有符号十进制整数 | %e / %E |
用科学记数法表示的浮点数(小写/大写) |
%u |
无符号十进制整数 | %f |
浮点数(float 或 double) |
%o |
八进制无符号整数 | %c |
单个字符 |
%x / %X |
十六进制无符号整数(小写/大写) | %s |
字符串(字符数组) |
常用格式修饰符:
%5d:右对齐输出至少占用 5 位宽度的整数。若实际位数不足 5 位,左侧用空格补齐;若实际位数超过 5 位,则按实际位数完整输出。%-5d:左对齐输出至少占用 5 位宽度的整数。若实际位数不足 5 位,右侧用空格补齐;若实际位数超过 5 位,则按实际位数完整输出。%05d:右对齐输出至少占用 5 位宽度的整数。若实际位数不足 5 位,左侧用字符'0'补齐;若实际位数超过 5 位,则按实际位数完整输出。%+d:输出整数时,无论正数还是负数,都显式输出其正负号。%.2f:保留 2 位小数输出浮点数。若小数部分超过 2 位则四舍五入;若不足 2 位则在末尾用0补全。
2. 格式化输入:scanf / fscanf
用于从标准输入或文件中读取变量值。
scanf("格式字符串", &变量1, &变量2, ...);
// 或者从指定文件指针 fin 中读取
fscanf(fin, "格式字符串", &变量1, &变量2, ...);
- 地址符
&警告:在使用scanf或fscanf读入基本类型变量(如int,double,char等)时,千万不要漏掉取地址运算符&。因为printf传递的是变量的值,而scanf必须接收变量的内存地址才能将读入的值写入。 - 返回值说明:
scanf和fscanf的返回值是成功输入并赋值的变量个数。若读取到文件末尾无数据可读时,fscanf会返回常量值EOF(通常定义为-1)。 - 分界符特征:
scanf和fscanf会自动忽略前导的制表符(TAB)、空格和回车。在读取数据时,一旦遇到这些空白字符,就会停止当前变量的读取。
3. 读取单个字符:fgetc
- 使用
fgetc(fin)可以从输入流中读取下一个字符。 - 正确流程:在读取时,应该先将返回值存入
int类型的变量中,并首先判断该返回值是否等于EOF。在确认不是EOF后,再通过强制类型转换将其转换为char类型使用。 - 跨平台换行符差异:
不同操作系统在文本文件中的行末换行符标准并不统一,处理单字符读入时需特别注意:
- Linux 系统:换行符为
\n(ASCII 10)。 - macOS 系统:换行符为
\r(ASCII 13)。 - Windows 系统:换行符由连续的两个字符组成,依次为
\r和\n。
- Linux 系统:换行符为
5.2 使用流输入/输出(C++ 风格)
C++ 提供了更加类型安全、书写简便的输入输出流。
* 头文件:<iostream>
1. 基础读写操作
- 输入到变量:
cin >> n; - 输出到屏幕:
cout << a; - 连续链式读写:
cpp cin >> n >> m; cout << a << ',' << b << "\n"; - 关于换行:在输出换行时,建议直接输出字符串
"\n"。由于std::endl在换行的同时会强行刷新输出缓冲区(flush),在大量输出时会导致效率显著降低。
2. 格式化输出控制
- 头文件:
<iomanip>
长度与对齐控制:
若要实现右对齐、占用 $n$ 位长度、不足部分用特定字符补齐,可以按如下方式调用:
cout.width(n);
cout.fill(' '); // 如果想用 "0" 补齐,可以将空格换成 '0'
cout << a;
注意:
cout.width(n)仅对紧随其后的那一次输出生效,每次输出前都需要重新调用。
输出多进制数:
C++ 允许通过流操纵算子直接改变整数的输出进制:
* 八进制输出:cout << oct << a;
* 十六进制输出:cout << hex << a;
* 其余进制(如二进制、三进制等)需要手动编写转换算法实现。
3. 性能优化提示
在数据规模非常大(例如读入或输出量达到 $10^5$ 级别以上)时,C++ 默认的输入输出流由于需要与 C 的标准输入输出保持同步,速度会变得非常慢,极易导致程序运行超时(TLE)。
在 main 函数的开头加上以下语句,可以取消同步并解除 cin 与 cout 的绑定,使流读写速度达到与 scanf 和 printf 相当的水平:
ios::sync_with_stdio(false);
cin.tie(nullptr);
安全警告:调用
ios::sync_with_stdio(false)之后,禁止在同一个程序中混合使用 C 风格的输入输出(如scanf/printf)和 C++ 风格的输入输出(如cin/cout),否则会导致输出顺序错乱。
5.3 课后推荐练习题
通过以下题目,可以练习各种输入输出格式(如保留小数、指定格式对齐、应对大输入量时的效率优化等):
- 题目编号:1 —— A+B Problem
- 考点:最基础的输入与输出。可以使用
scanf/printf或cin/cout来完成两数读入和求和输出。
- 考点:最基础的输入与输出。可以使用
- 题目编号:3 —— 读入一个浮点数,保留 2 位小数
- 考点:练习控制浮点数输出精度。使用 C 风格的
%.2f或 C++ 风格的fixed与setprecision(2)进行格式化输出。
- 考点:练习控制浮点数输出精度。使用 C 风格的
- 题目编号:7 —— 计算圆的面积
- 考点:需要根据输入的半径计算面积并精确保留四位小数,适合练习精度控制修饰符。
- 题目编号:12 —— 钞票分解
- 考点:题目对输出格式有严格要求,需要输出固定的拼写与空格,非常适合练习整除、求模以及
printf格式化字符串的精确排版。
- 考点:题目对输出格式有严格要求,需要输出固定的拼写与空格,非常适合练习整除、求模以及
- 题目编号:166 —— 区间和询问
- 考点:本题的数据规模较大($N, M \le 100,000$)。如果不进行输入输出优化,使用未经加速的
cin和cout极易超时。适合练习scanf/printf或流加速语句。
- 考点:本题的数据规模较大($N, M \le 100,000$)。如果不进行输入输出优化,使用未经加速的
- 题目编号:228 —— 数字金字塔
- 考点:需要按照金字塔的形状进行对齐输出,每一行前的空格数和数字间的空格有着严格的排版规律,是练习输出宽度控制的优秀题目。
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com