C++复健笔记 Vol.6:保研夏令营机考 IO 备考指南
摘要
面向保研夏令营机考的 C++ IO 复习:不用万能头,少做非必要优化,重点避开 getline、EOF、格式化输出和本地调试坑。
这一篇重新整理一下 C++ 的 IO。目标不是把标准库流讲完整,而是面向保研夏令营机考:能稳、能默写、少踩坑。
也就是说,我不再把 #include <bits/stdc++.h> 和快 IO 当成默认模板。机考环境可能是 Dev-C++ 或比较老的编译器,万能头不一定稳;大部分夏令营题的数据量也没有大到必须先优化 IO。
头文件:不用万能头
考场上最稳妥的写法是只引入需要的标准头文件。
| 需要使用的功能 | 头文件 |
|---|---|
cin / cout | #include <iostream> |
string 字符串 | #include <string> |
stringstream 字符串流解析 | #include <sstream> |
vector 动态数组 | #include <vector> |
sort / reverse 等算法 | #include <algorithm> |
setprecision 保留小数 | #include <iomanip> |
toupper / isdigit 字符处理 | #include <cctype> |
freopen / printf | #include <cstdio> |
考场常用模板可以先背这一版:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
int main() {
// code here
return 0;
}
如果题目需要字符判断,再加 #include <cctype>;如果本地调试要用 freopen,再加 #include <cstdio>。
OJ 输入和 EOF
OJ 判题时,系统会把测试数据重定向到程序的标准输入里。对代码来说,就是从 cin 读数据。
文件一定有结尾,所以这种写法会在读到 EOF 时自动停下:
int x;
while (cin >> x) {
cout << x << '\n';
}
如果是一行一行读,也一样:
string line;
while (getline(cin, line)) {
cout << line << '\n';
}
本地在 Dev-C++ 黑框里手动测试时,如果想模拟 EOF,Windows 下一般是输入完数据后按 Ctrl + Z,再按回车。
注意,不要写这种循环:
while (!cin.eof()) {
cin >> x;
// ...
}
更稳的写法是把“读入动作”直接放进循环条件:
while (cin >> x) {
// ...
}
不定长数据:getline + stringstream
如果题目说每一行有不定数量的整数,最舒服的组合是 getline + stringstream。
比如:读入若干行,每行有若干整数,输出每行的和。
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main() {
string line;
while (getline(cin, line)) {
if (line.empty()) {
continue;
}
stringstream ss(line);
int sum = 0;
int x;
while (ss >> x) {
sum += x;
}
cout << sum << '\n';
}
return 0;
}
这个模板很适合处理:
- 每行数字个数不固定;
- 输入行数没有提前给出;
- 每一行都要单独统计或分析。
cin 和 getline 混用
cin >> x 读走数据,但通常不会读走行末的换行符。紧接着写 getline(cin, line),就可能直接读到一个空字符串。
典型场景:先读一个整数 T,后面跟着 T 行字符串,而且字符串里可能有空格。
int T;
cin >> T;
cin.ignore();
string line;
for (int i = 0; i < T; i++) {
getline(cin, line);
// 处理 line
}
这里的 cin.ignore() 不能忘,它负责吃掉 T 后面的那个回车。
如果担心这一行里还有别的残留内容,可以写更完整的版本:
#include <limits>
cin.ignore(numeric_limits<streamsize>::max(), '\n');
不过考场上,多数题只是 cin >> T 后接整行字符串,cin.ignore() 通常够用。
字符串拆分
如果一行数据按空格分隔,用 stringstream >> 就够了:
string line = "Tom 90 85 92";
stringstream ss(line);
string name;
int a, b, c;
ss >> name >> a >> b >> c;
如果按逗号这种单字符分隔,可以用 getline 的第三个参数:
string line = "Tom,90,85,92";
stringstream ss(line);
string part;
while (getline(ss, part, ',')) {
cout << part << '\n';
}
如果有多种分隔符,比如空格、逗号、句号混在一起,可以用 find_first_of。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<string> splitMulti(const string& s, const string& delimiters) {
vector<string> res;
size_t start = 0;
while (start < s.size()) {
size_t end = s.find_first_of(delimiters, start);
string word = s.substr(start, end - start);
if (!word.empty()) {
res.push_back(word);
}
if (end == string::npos) {
break;
}
start = end + 1;
}
return res;
}
用法:
vector<string> words = splitMulti("hello, world. C++", " ,.");
这种写法不算短,但很稳定。考场上如果分隔符复杂,不要硬用一堆 if 手搓。
格式化输出
保留小数用 <iomanip>:
#include <iomanip>
double x = 3.1415926;
cout << fixed << setprecision(2) << x << '\n'; // 3.14
注意,fixed 和 setprecision(2) 设置后,会影响后续的浮点数输出。
cout << fixed << setprecision(2);
cout << 3.1415926 << '\n'; // 3.14
cout << 2.0 << '\n'; // 2.00
如果考场上实在忘了 <iomanip>,也可以用 C 风格输出:
#include <cstdio>
printf("%.2f\n", x);
但不要一会儿用 cout 的格式控制,一会儿又切到 printf。能统一就统一。
vector 输出不要多空格
很多 OJ 对行尾空格不严格,但不要把希望寄托在“它应该能过”上。输出数组时,最稳的是用下标控制空格。
vector<int> v = {1, 2, 3, 4};
for (int i = 0; i < (int)v.size(); i++) {
if (i > 0) {
cout << ' ';
}
cout << v[i];
}
cout << '\n';
核心就是:从第二个元素开始,先输出空格,再输出元素。
文件 IO 和本地调试
OJ 提交时,正常情况下不需要写文件 IO,也不需要写 freopen。直接用 cin 和 cout,判题系统会自动处理输入输出。
本地调试时,可以在 .cpp 同目录下放一个 1.in 文件,然后临时加:
#include <cstdio>
freopen("1.in", "r", stdin);
完整位置一般放在 main 一开始:
int main() {
freopen("1.in", "r", stdin);
// code here
return 0;
}
提交前一定要删掉或注释掉这一行:
// freopen("1.in", "r", stdin);
否则 OJ 上没有这个本地文件,轻则读不到数据,重则直接报错。
终极 Clean 模板
如果题目是“先给 T,再给 T 行可能包含空格的内容”,可以套这个:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
int main() {
// 本地调试时再打开,提交前删掉或注释掉
// freopen("1.in", "r", stdin);
int T;
cin >> T;
cin.ignore();
while (T--) {
string line;
getline(cin, line);
// 处理 line
}
return 0;
}
如果打开了 freopen 那行,记得补 #include <cstdio>。
如果题目没有给行数,直接读到 EOF:
string line;
while (getline(cin, line)) {
// 处理 line
}
考前一分钟自查
- 有没有删掉
#include <bits/stdc++.h>? - 有没有删掉或注释掉本地调试用的
freopen? - 先读数字再读整行时,有没有写
cin.ignore()? - 需要读入含空格的字符串时,有没有用
getline? - 输出浮点数时,有没有加
#include <iomanip>? - 输出
vector时,末尾有没有多余空格? - 读到 EOF 时,是不是写成了
while (cin >> x)或while (getline(cin, line))? - 题目要求按行处理时,有没有避免直接用
cin >> s把空格后的内容丢掉?
小结
这篇的重点不是“C++ IO 有多少种玩法”,而是机考时怎么少丢分:
- 标准头文件比万能头更稳。
- 简单
cin/cout已经够应付大多数夏令营机考题。 getline和cin >>混用时,先处理掉残留换行。- 不定长行数据优先想
getline + stringstream。 - 输出格式、尾随空格、本地
freopen都是小坑,但每一个都可能让人白丢分。