【C++】流式数据输入处理(不完全整理)

纯 C++ 流式数据输入处理

1、单组简单数据

1.1 单个整数、浮点数、字符、字符串

说明: cin >> 自动跳过前导空白符(空格、制表符、换行),读取到下一个空白符为止。

cpp 复制代码
#include<iostream>
#include<string>
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;          // 整数
    double d;       // 浮点数
    char ch;        // 单个字符
    std::string s;  // 字符串(不含空格)

    std::cin >> n >> d >> ch >> s;

    // 输出验证读取结果
    std::cout << "int: " << n << '\n';
    std::cout << "double: " << d << '\n';
    std::cout << "char: " << ch << '\n';
    std::cout << "string: " << s << '\n';

    return 0;
}
plaintext 复制代码
42 3.14 X hello_world

1.2 一行内多个同类型数据(已知个数)

说明: 先读个数 N,再用循环依次读入 N 个同类型数据。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;  // 先读入数据个数

    std::vector<int> a(n);
    for (int i = 0; i < n; i++)
    {
        std::cin >> a[i];   // 依次读入每个整数
    }

    // 输出验证
    std::cout << "读取了 " << n << " 个整数:";
    for (int i = 0; i < n; ++i)
    {
        std::cout << ' ' << a[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
5
10 20 30 40 50

1.3 一行内不定个数的整数,直到行尾

说明: 先用 getline 读入整行,再用 istringstream 逐个解析整数。

cpp 复制代码
#include<iostream>
#include<sstream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::string line;
    std::getline(std::cin, line);   // 读入整行

    std::istringstream iss(line);   // 将整行放入字符串流
    std::vector<int> nums;
    int x;
    while (iss >> x)    // 逐个解析整数,直到流结束
    {
        nums.push_back(x);
    }

    // 输出验证
    std::cout << "本行共读取 " << nums.size() << " 个整数:";
    for (std::size_t i = 0; i < nums.size(); ++i)
    {
        std::cout << ' ' << nums[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
7 14 21 28 35 42 49 56 63

2. 单组多行数据

2.1 给定行数 N,接下来 N 行每行一个数据

说明: 先读 N,再循环 N 次,每次读入一个数据。cin >> 会自动跳过换行。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;  // 读入行数

    std::vector<int> data(n);
    for (int i = 0; i < n; i++)
    {
        std::cin >> data[i];
    }

    // 输出验证
    std::cout << "读取了 " << n << " 行数据:\n";
    for (int i = 0; i < n; i++)
    {
        std::cout << " [" << i << "] = " << data[i] << '\n';
    }

    return 0;
}
plaintext 复制代码
4
100
200
300
400

2.2 不定行数,直到 EOF(整数)

说明: 利用 cin >> 的返回值判断是否成功读取,读到 EOF 时自动停止。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::vector<int> nums;
    int x;
    // cin >> x 在读到 EOF 或类型不匹配时返回 false,循环自动终止
    while (std::cin >> x)
    {
        nums.push_back(x);
    }

    // 输出验证
    std::cout << "共读取 " << nums.size() << " 个整数:";
    for (std::size_t i = 0; i < nums.size(); i++)
    {
        std::cout << ' ' << nums[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
11
22
33
44
55
(Windows) Ctrl+Z 然后回车输入 EOF 信号

2.3 不定行数,直到 EOF(整行字符串)

说明:getline 逐行读取,空行也会被读入(内容为空字符串)。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::vector<std::string> lines;
    std::string line;
    // getline 在遇到 EOF 时返回false,循环自动终止
    while (std::getline(std::cin, line))
    {
        lines.push_back(line);
    }

    // 输出验证(含空行)
    std::cout << "共读取 " << lines.size() << " 行:\n";
    for (std::size_t i = 0; i < lines.size(); ++i)
    {
        std::cout << "  line[" << i << "] = \"" << lines[i] << "\"\n";
    }

    return 0;
}
plaintext 复制代码
Hello World

This is a test
Goodbye
(Windows) Ctrl+Z 然后回车输入 EOF 信号

3. 多组测试用例

3.1 第一行给出 T,然后循环 T 次

说明: 最标准的多组测试用例格式,先读 T,再用 for 循环处理每组数据。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int T;
    std::cin >> T;  // 读入测试用例总数

    for (int t = 1; t <= T; t++)
    {
        int n;
        std::cin >> n;  // 每组用例先读元素个数
        std::vector<int> a(n);
        for (int i = 0; i < n; i++)
        {
            std::cin >> a[i];
        }

        std::cout << "Case #" << t << ": n=" << n << ", data:";
        for (int i = 0; i < n; i++)
        {
            std::cout << ' ' << a[i];
        }
        std::cout << '\n';
    }


    return 0;
}
plaintext 复制代码
3
2
10 20
3
100 200 300
1
42

3.2 未给出 T,读取到 EOF 结束

说明:while(cin >> n) 判断是否还有新用例,读到 EOF 自动退出。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    int caseNo = 0;
    // 每次循环读取一个新用例的 N,读到 EOF 时终止
    while (std::cin >> n)
    {
        caseNo++;
        std::vector<int> a(n);
        for (int i = 0; i < n; i++)
        {
            std::cin >> a[i];
        }

        // 输出验证
        std::cout << "Case #" << caseNo << ": n=" << n << ", data:";
        for (int i = 0; i < n; i++)
        {
            std::cout << ' ' << a[i];
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
2
10 20
3
100 200 300
1
42

3.3 以特殊标记结束(如 0 0

说明: 每次读取后检查是否为终止标记,若是则 break 退出循环。

cpp 复制代码
#include <iostream>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int a, b;
    int caseNo = 0;
    while (std::cin >> a >> b)
    {
        if (a == 0 && b == 0) break;
        caseNo++;
        std::cout << "Case " << caseNo << ": a=" << a << ", b=" << b << '\n';
    }

    return 0;
}
plaintext 复制代码
3 5
10 20
7 8
0 0

3.4 以单词 "stop" 结束

说明: 逐个读入字符串,遇到特定终止单词时退出循环。

cpp 复制代码
#include<iostream>
#include<string>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::string word;
    int count = 0;
    while (std::cin >> word)
    {
        if (word == "stop") break;
        count++;
        std::cout << "[" << count << "] " << word << '\n';
    }

    std::cout << "共读取 " << count << " 个单词\n";

    return 0;
}
plaintext 复制代码
apple banana cherry date stop

3.5 嵌套结束:每组用例内含以 0 结束的子序列

说明: 外层以 n=0 终止用例,内层每组子序列以 0 终止。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    int caseNo = 0;
    // 外层:读取 n,n=0 时终止所有用例
    while (std::cin >> n && n != 0)
    {
        caseNo++;
        std::vector<std::vector<int>> groups;

        for (int i = 0; i < n; i++)
        {
            std::vector<int> seq;
            int val;
            // 内层:读取子序列,遇到 0 终止当前子序列
            while (std::cin >> val && val != 0)
            {
                seq.push_back(val);
            }
            groups.push_back(seq);
        }

        // 输出验证
        std::cout << "Case " << caseNo << " (n=" << n << "):\n";
        for (int i = 0; i < n; i++)
        {
            std::cout << "  group[" << i << "]:";
            for (std::size_t j = 0; j < groups[i].size(); j++)
            {
                std::cout << ' ' << groups[i][j];
            }
            std::cout << '\n';
        }
    }

    return 0;
}
plaintext 复制代码
2
1 2 3 0
4 5 0
3
10 0
20 30 40 0
50 60 0
0

4. 二维数组 / 矩阵

4.1 给定 N 行 M 列,读 N×M 个元素

说明: 标准的二维矩阵读入方式,用二重循环逐个读入。cin >> 自动跳过换行。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m; // 读入行数和列数

    // 创建 N×M 的二维矩阵
    std::vector<std::vector<int>> mat(n, std::vector<int>(m));
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            std::cin >> mat[i][j];  // 逐个读入矩阵元素
        }
    }

    // 输出验证
    std::cout << "矩阵 " << n << "x" << m << ":\n";
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (j > 0) std::cout << '\t';
            std::cout << mat[i][j];
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
3 4
1 2 3 4
5 6 7 8
9 10 11 12

4.2 仅给定行 N,每行列数不定(锯齿形矩阵)

说明: 先读 N,再逐行 getline,用 istringstream 解析每行的不定个数整数。注意 cin >> n 后需 ignore 掉残留换行符。

cpp 复制代码
#include<iostream>
#include<sstream>
#include<string>
#include<vector>
#include<limits>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    // 清除 cin >> n 后残留的换行符,否则 getline 会读到空行
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::vector<std::vector<int>> jagged(n);
    for (int i = 0; i < n; i++)
    {
        std::string line;
        std::getline(std::cin, line);   // 读入整行

        std::istringstream iss(line);   // 解析行内整数
        int val;
        while (iss >> val)
        {
            jagged[i].push_back(val);
        }
    }

    // 输出验证
    std::cout << "锯齿形矩阵 (" << n << " 行):\n";
    for (int i = 0; i < n; i++)
    {
        std::cout << "  row[" << i << "] (" << jagged[i].size() << " 个):";
        for (std::size_t j = 0; j < jagged[i].size(); j++)
        {
            std::cout << ' ' << jagged[i][j];
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
4
1 2 3
4 5
6 7 8 9 10
11

4.3 字符矩阵(逐字符读入,跳过空白)

说明:cin >> char 逐个读入字符,自动跳过空格和换行。适用于字符之间有空格分隔的矩阵。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<char>> grid(n, std::vector<char>(m));
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            std::cin >> grid[i][j]; // 读入单个字符,自动跳过空白
        }
    }

    // 输出验证
    std::cout << "字符矩阵 " << n << "×" << m << ":\n";
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            std::cout << grid[i][j];
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
3 5
. # . # .
# . # . #
. . # . .

4.4 字符矩阵(逐行字符串读入,保留所有字符)

说明:getline 逐行读入,保留行内所有字符(包括空格)。适用于字符之间无空格、需要保留空白字符的场景。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>
#include<limits>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;
    // 清除 cin >> n >> m 后的残留换行
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::vector<std::string> grid(n);
    for (int i = 0; i < n; i++)
    {
        std::getline(std::cin, grid[i]);    // 读入整行字符串
    }

    // 输出验证
    std::cout << "字符矩阵 " << n << "×" << m << ":\n";
    for (int i = 0; i < n; i++)
    {
        std::cout << "  row[" << i << "] = \"" << grid[i] << "\" (len=" << grid[i].size() << ")\n";
    }

    return 0;
}
plaintext 复制代码
3 5
.#.#.
#.#.#
..#..

5. 字符串输入

5.1 读取单词(不含空格)

说明: cin >> string 自动跳过前导空白,读到下一个空白符为止。

cpp 复制代码
#include <iostream>
#include <string>

int main() 
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::string word;
    std::cin >> word;  // 只读一个单词,前导空白被自动跳过

    std::cout << "word = \"" << word << "\"\n";

    return 0;
}
plaintext 复制代码
   HelloWorld

5.2 读取整行(含空格),注意残留换行符

说明: cin >> 后必须用 ignore 清除残留换行符,否则 getline 会读到空行。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int id;
    std::cin >> id;
    // 关键:清除 cin >> id 后的残留换行符
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::string line;
    std::getline(std::cin, line);   // 现在能正常读入整行
    
    // 输出验证
    std::cout << "id = " << id << '\n';
    std::cout << "line = \"" << line << "\"\n";

    return 0;
}
plaintext 复制代码
42
Hello World, this is a test!

5.3 连续读取多行直到 EOF(含空行)

说明:while(getline) 循环,空行会被读入为空字符串 "",不会跳过。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::vector<std::string> lines;
    std::string line;
    while (std::getline(std::cin, line))
    {
        lines.push_back(line);  // 空行也会被存入
    }

    // 输出验证(空行显示为空字符串)
    for (std::size_t i = 0; i < lines.size(); i++)
    {
        std::cout << "[" << i << "] \"" << lines[i] << "\"\n";
    }

    return 0;
}
plaintext 复制代码
First line

Third line after empty line


Two empty lines above
(Windows) Ctrl+Z 然后回车输入 EOF 信号

5.4 读取字符数组(不推荐,但兼容场景)

说明: cin.getline(buf, size) 是 C++ istream 成员函数(非 C 风格),读入最多 size-1 个字符到 char 数组中,以 '\0' 结尾。推荐使用 std::string + std::getline 替代。

cpp 复制代码
#include<iostream>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    char buf[256];
    std::cin.getline(buf, 256); // 读入整行到字符数组

    std::cout << "buf = \"" << buf << "\"\n";

    return 0;
}
plaintext 复制代码
This is a char array test with spaces.

6. 复合结构

6.1 一行包含混合类型数据

说明: cin >> 可链式读取不同类型的变量,自动按类型解析。

cpp 复制代码
#include<iostream>
#include<string>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int id;
    double score;
    std::string name;

    // 一行内一次读入 int double string
    std::cin >> id >> score >> name;

    // 输出验证
    std::cout << "id=" << id << ", score=" << score << ", name=" << name << '\n';

    return 0;
}
plaintext 复制代码
1001 95.5 Alice

6.2 N 行每行 string int double 组合

说明: 先读 N,再循环 N 次,每次读入一组混合类型数据。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<std::string> names(n);
    std::vector<int> ages(n);
    std::vector<double> scores(n);

    for (int i = 0; i < n; i++)
    {
        // 每行一次读入姓名、年龄、分数
        std::cin >> names[i] >> ages[i] >> scores[i];
    }

    // 输出验证
    for (int i = 0; i < n; i++)
    {
        std::cout << "[" << i << "] " << names[i] << ", age=" << ages[i] << ", score=" << scores[i] << '\n';
    }

    return 0;
}
plaintext 复制代码
3
Alice 20 92.5
Bob 22 88.0
Charlie 21 95.3

6.3 读入 N 对数据(使用 std::pair

说明: 先读 N,再循环 N 次,每次读入一组混合类型数据。

cpp 复制代码
#include<iostream>
#include<vector>
#include<utility>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<std::pair<int, int>> data(n);
    for (int i = 0; i < n; i++)
    {
        std::cin >> data[i].first >> data[i].second;    // 读入一对数据
    }

    // 输出验证
    std::cout << n << " 对数据:\n";
    for (int i = 0; i < n; i++)
    {
        std::cout << "  pair[" << i << "] = (" << data[i].first << ", " << data[i].second << ")\n";
    }

    return 0;
}
plaintext 复制代码
4
1 10
2 20
3 30
4 40

7. 树与图的输入

7.1 无向树(N 个节点,N-1 条边,1-based)

说明: 读入 N-1 条边,每条边连接两个节点,构建无向邻接表。节点编号从 1 开始,因此邻接表大小为 N+1。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    // 节点编号 1~n,邻接表大小为 n+1
    std::vector<std::vector<int>> adj(n + 1);
    for (int i = 0; i < n - 1; i++)
    {
        int u, v;
        std::cin >> u >> v;
        adj[u].push_back(v);    // 无向边,双向存储
        adj[v].push_back(u);
    }

    // 输出验证
    std::cout << "无向树邻接表 (n=" << n << "):\n";
    for (int i = 1; i <= n; ++i) 
    {
        std::cout << "  node " << i << ":";
        for (std::size_t j = 0; j < adj[i].size(); ++j) 
        {
            std::cout << ' ' << adj[i][j];
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
6
1 2
1 3
2 4
2 5
3 6

7.2 有向带权图(N 个节点,M 条边)

说明: 读入 M 条有向边,每条边包含起点、终点、权重,构建邻接表。

cpp 复制代码
#include<iostream>
#include<vector>
#include<utility>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;

    // 邻接表矩阵:每个节点对应一组(终点,权重)
    std::vector<std::vector<std::pair<int, int>>> adj(n + 1);
    for (int i = 0; i < m; i++)
    {
        int u, v, w;
        std::cin >> u >> v >> w;
        adj[u].push_back({ v, w });    // 有向边,只存单向
    }

    // 输出验证
    std::cout << "有向带权图邻接表 (n=" << n << ", m=" << m << "):\n";
    for (int i = 1; i <= n; ++i) 
    {
        std::cout << "  node " << i << ":";
        for (std::size_t j = 0; j < adj[i].size(); ++j) 
        {
            std::cout << " ->(" << adj[i][j].first
                << ", w=" << adj[i][j].second << ")";
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
5 6
1 2 10
1 3 5
2 4 3
3 2 2
3 5 8
5 4 1

8. 其他典型输入

8.1 第一行 N 和 K,然后一行 N 个整数

说明: 最常见的竞赛输入格式之一,先读两个参数,再读数组。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, k;
    std::cin >> n >> k;        // 读入数组大小和参数K

    std::vector<int> a(n);
    for (int i = 0; i < n; i++)
    {
        std::cin >> a[i];
    }

    // 输出验证
    std::cout << "n=" << n << ", k=" << k << '\n';
    std::cout << "数组:";
    for (int i = 0; i < n; ++i) 
    {
        std::cout << ' ' << a[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
7 3
1 5 2 8 3 7 4

8.2 包含前导零的数据(电话号码 / 身份证号等,必须用 string

说明: 前导零在整数类型中会被丢弃,因此必须用 string 存储。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<std::string> ids(n);
    for (int i = 0; i < n; i++)
    {
        std::cin >> ids[i];    // 用 string 保留前导零
    }

    // 输出验证
    std::cout << "读取了 " << n << " 个编号:\n";
    for (int i = 0; i < n; ++i) 
    {
        std::cout << "  [" << i << "] = " << ids[i] << '\n';
    }

    return 0;
}
plaintext 复制代码
3
001234
00000001
110101199001011234

8.3 逗号分隔的数据

说明: 先用 getline 读入整行,再用 getline(iss, token, ',') 按逗号分割。

cpp 复制代码
#include<iostream>
#include<sstream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::string line;
    std::getline(std::cin, line);    // 读入整行

    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;
    while (std::getline(iss, token, ','))
    {
        tokens.push_back(token);
    }

    // 尝试将每个 token 解析为整数
    std::vector<int> nums;
    for (std::size_t i = 0; i < tokens.size(); i++)
    {
        std::istringstream tokenStream(tokens[i]);
        int val;
        if (tokenStream >> val)
        {
            nums.push_back(val);
        }
    }

    // 输出验证
    std::cout << "字符串 tokens:";
    for (std::size_t i = 0; i < tokens.size(); ++i) 
    {
        std::cout << " \"" << tokens[i] << "\"";
    }
    std::cout << '\n';

    std::cout << "解析后的整数:";
    for (std::size_t i = 0; i < nums.size(); ++i) 
    {
        std::cout << ' ' << nums[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
10,20,30,40,50

8.4 极大数据量(快速 I/O 演示)

说明: ios::sync_with_stdio(false)cin.tie(nullptr) 是必须的两行加速代码,使 cin 速度与 scanf 相当。

cpp 复制代码
#include<iostream>
#include<vector>

int main() 
{
    // 关键:关闭 C/C++ 标准流同步,大幅提升 I/O 速度
    std::ios::sync_with_stdio(false);
    // 解除 cin 与 cout 的绑定,避免每次 cin 前自动 flush cout
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) 
    {
        std::cin >> a[i];
    }

    // 输出验证(仅展示前 5 个)
    std::cout << "读取了 " << n << " 个整数\n";
    for (int i = 0; i < n && i < 5; ++i) 
    {
        std::cout << "  a[" << i << "] = " << a[i] << '\n';
    }
    if (n > 5) 
    {
        std::cout << "  ... (省略 " << (n - 5) << " 个)\n";
    }

    return 0;
}
plaintext 复制代码
10
999888777 111222333 444555666 777888999 123456789 987654321 111111111 222222222 333333333 444444444

9. 健壮性与常见陷阱演示

9.1 cin >>getline 混用的换行符残留问题

说明: cin >> 读完数字后,换行符 '\n' 仍留在流中,若不 ignore,getline 会立即读到空字符串。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    // 关键:清除 cin >> n 后残留的换行符
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::string name;
    std::getline(std::cin, name);    // 现在能正确读入带空格的姓名

    // 输出验证
    std::cout << "n = " << n << '\n';
    std::cout << "name = \"" << name << "\"\n";

    return 0;
}
plaintext 复制代码
5
Zhang San

9.2 流状态清除(输入类型错误时的恢复)

说明:cin >> int 遇到非数字时,流进入 fail 状态。必须先 clear() 恢复流状态,再 ignore() 跳过错误输入。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    std::vector<int> nums;
    int x;
    int maxCount = 5;    // 最多读取 5 个有效整数

    while (maxCount > 0)
    {
        if (std::cin >> x)
        {
            nums.push_back(x);    // 读取成功
            maxCount--;
        }
        else
        {
            // 读取失败:先清除错误状态,再跳过当前行的剩余内容
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
    }

    // 输出验证
    std::cout << "成功读取的整数:";
    for (std::size_t i = 0; i < nums.size(); ++i) {
        std::cout << ' ' << nums[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
10
abc
20
xyz
30
40
50

9.3 EOF 判断与流状态的综合说明

说明: while(cin >> x) 是最安全的读法,同时处理 EOF 和类型错误。cin.eof() 可用于区分"正常结束"和"输入错误"。

cpp 复制代码
#include<iostream>
#include<string>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int x;
    while (std::cin >> x)
    {
        std::cout << "读取整数: " << x << '\n';
    }

    // 判断循环终止原因
    if (std::cin.eof())
    {
        std::cout << "已到达文件末尾 (EOF)\n";
    }
    else
    {
        std::cout << "输入类型错误或流异常\n";
    }

    return 0;
}
plaintext 复制代码
100
200
300

10. 综合实战:多组用例 + 混合输入类型

说明: 演示在单个程序中混合使用 cin >>getlineistringstreamignore,处理包含标题、整数数组、浮点数行、图边等多种输入的复杂场景。

cpp 复制代码
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <utility>
#include <limits>

int main() 
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int T;
    std::cin >> T;

    for (int t = 1; t <= T; ++t) 
    {
        // 清除残留换行
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

        // 1. 读取用例标题(含空格,必须用 getline)
        std::string title;
        std::getline(std::cin, title);

        // 2. 读取 n 和 k
        int n, k;
        std::cin >> n >> k;

        // 3. 读取 n 个整数的数组
        std::vector<int> a(n);
        for (int i = 0; i < n; ++i) 
        {
            std::cin >> a[i];
        }

        // 4. 清除数组行末尾换行,再读一行浮点数
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::string line;
        std::getline(std::cin, line);
        std::istringstream iss(line);
        std::vector<double> vals;
        double d;
        while (iss >> d) 
        {
            vals.push_back(d);
        }

        // 5. 读取图边数据
        int m;
        std::cin >> m;
        std::vector<std::pair<std::pair<int, int>, int>> edges(m);
        for (int i = 0; i < m; ++i) 
        {
            std::cin >> edges[i].first.first
                >> edges[i].first.second
                >> edges[i].second;
        }

        // 输出验证
        std::cout << "=== Case " << t << " ===\n";
        std::cout << "Title: " << title << '\n';
        std::cout << "n=" << n << ", k=" << k << '\n';
        std::cout << "Array:";
        for (int i = 0; i < n; ++i) std::cout << ' ' << a[i];
        std::cout << '\n';
        std::cout << "Doubles:";
        for (std::size_t i = 0; i < vals.size(); ++i) std::cout << ' ' << vals[i];
        std::cout << '\n';
        std::cout << "Edges (m=" << m << "):\n";
        for (int i = 0; i < m; ++i) 
        {
            std::cout << "  " << edges[i].first.first << " -> "
                << edges[i].first.second
                << " (w=" << edges[i].second << ")\n";
        }
    }

    return 0;
}
plaintext 复制代码
2
Graph Problem Alpha
3 2
10 20 30
1.5 2.5 3.5 4.5
2
1 2 10
2 3 20
String Query Beta
4 1
5 6 7 8
9.9 8.8
1
1 4 99

11. 罕见输入格式

11.1 处理括号/特殊字符包裹的坐标数据(如 (x,y)

说明: 先用 cin >> 读入整个 token,将非数字/负号字符替换为空格,再用 istringstream 解析出两个整数坐标。

cpp 复制代码
#include<iostream>
#include<sstream>
#include<string>
#include<vector>
#include<utility>
#include<cctype>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<std::pair<int, int>> coords(n);
    for (int i = 0; i < n; i++)
    {
        std::string token;
        std::cin >> token;    // 读入如 "(10,20)" 的整体

        // 将非数字、非负号的字符替换为空格
        for (std::size_t j = 0; j < token.size(); j++)
        {
            char c = token[j];
            if (!std::isdigit(c) && c != '-')
            {
                token[j] = ' ';
            }
        }

        // 从清理后的字符串中解析两个整数
        std::istringstream iss(token);
        iss >> coords[i].first >> coords[i].second;
    }

    // 输出验证
    std::cout << "解析后的坐标:\n";
    for (int i = 0; i < n; ++i) 
    {
        std::cout << "  [" << i << "] = ("
            << coords[i].first << ", "
            << coords[i].second << ")\n";
    }

    return 0;
}
plaintext 复制代码
3
(10,20)
(-5,100)
(42,-99)

11.2 自定义分隔符读取(如分号 ; 或竖线 | 分隔)

说明: getline(iss, token, delim) 的第三个参数可指定任意分隔符。

cpp 复制代码
#include<iostream>
#include<sstream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::string line;
    std::getline(std::cin, line);    // 读入整行

    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 以 ';' 为分隔符逐个提取 token
    while (std::getline(iss, token, ';'))
    {
        tokens.push_back(token);
    }

    // 输出验证
    std::cout << "按分号分割后的 tokens:\n";
    for (std::size_t i = 0; i < tokens.size(); ++i) 
    {
        std::cout << "  [" << i << "] = \"" << tokens[i] << "\"\n";
    }

    return 0;
}
plaintext 复制代码
apple;banana;cherry;date;elderberry

11.3 跨行/带引号的长字符串 Token(含内部换行)

说明:cin.get(ch) 逐字符读取,先找到开头引号,再读到结尾引号为止,中间的所有字符(包括换行符)都会被保留。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<std::string> quotes(n);
    for (int i = 0; i < n; i++)
    {
        char ch;
        // 跳过直到遇到开头引号
        while (std::cin.get(ch) && ch != '"');

        // 读取引号内的所有内容,直到遇到结尾引号
        std::string current;
        while (std::cin.get(ch) && ch != '"')
        {
            current += ch;
        }
        quotes[i] = current;
    }

    // 输出验证
    std::cout << "读取了 " << n << " 个跨行引号字符串:\n";
    for (int i = 0; i < n; i++)
    {
        std::cout << "--- token " << i << " ---\n" << quotes[i] << "\n";
    }

    return 0;
}
plaintext 复制代码
2
"This is a
multi-line
string."
"Second one."

11.4 超大单行数据的优化读取(避免 string 频繁重分配)

说明: 对于超长行(如 MB 级别),预先 reserve 足够空间,避免 getline 内部多次扩容带来的性能损耗。

cpp 复制代码
#include<iostream>
#include<string>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::string huge_line;
    // 预分配 2MB 空间,避免 getline 过程中的多次内存重分配
    huge_line.reserve(2 * 1024 * 1024);
    
    std::getline(std::cin, huge_line);

    // 输出验证
    std::cout << "成功读取超大行,长度 = " << huge_line.size() << '\n';
    std::cout << "前 50 字符: \"";
    for (std::size_t i = 0; i < huge_line.size() && i < 50; ++i) 
    {
        std::cout << huge_line[i];
    }
    std::cout << "...\"\n";

    return 0;
}
plaintext 复制代码
ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()_+~`|}{[]:;?><,./-=

11.5 二进制数据块读取(极罕见,如读取自定义格式文件头)

说明: cin.read(buf, size) 精确读取 size 字节,cin.gcount() 返回实际读取字节数。

注意:需要确保流以二进制模式打开(文件重定向时由系统处理)。

cpp 复制代码
#include<iostream>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    const std::size_t HEADER_SIZE = 8;
    std::vector<char> buffer(HEADER_SIZE);

    // 精确读取 8 字节
    std::cin.read(buffer.data(), HEADER_SIZE);

    // gcount() 返回实际读取的字节数(可能少于请求数,如遇到 EOF)
    std::streamsize bytesRead = std::cin.gcount();
    std::cout << "期望读取 " << HEADER_SIZE << " 字节数,实际读取 " << bytesRead << " 字节数\n";

    // 十六进制 dump 输出
    std::cout << "十六进制 dump:\n";
    for (std::streamsize i = 0; i < bytesRead; i++)
    {
        unsigned char byte = static_cast<unsigned char>(buffer[i]);
        std::cout << std::hex << (byte < 16 ? "0" : "") << (int)byte << " ";
    }
    std::cout << std::dec << '\n';

    return 0;
}
plaintext 复制代码
HEAD1234

11.6 多次 cin >>getline 安全切换的范式

说明: 每次从 cin >> 切换到 getline 之前,必须用 ignore 清除残留换行符。本例演示了两次切换的完整流程。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;
    // 第一次切换前:清除残留换行
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::string title;
    std::getline(std::cin, title);    // 读取标题行

    std::vector<int> a(n);
    for (int i = 0; i < n; i++)
    {
        std::cin >> a[i];
    }
    // 第二次切换前:清除残留换行
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::string footer;
    std::getline(std::cin, footer);    // 读取尾部行

    // 输出验证
    std::cout << "n=" << n << ", m=" << m << '\n';
    std::cout << "title = \"" << title << "\"\n";
    std::cout << "array:";
    for (int i = 0; i < n; ++i) std::cout << ' ' << a[i];
    std::cout << '\n';
    std::cout << "footer = \"" << footer << "\"\n";

    return 0;
}
plaintext 复制代码
3 99
Graph Theory Problem
10 20 30
End of Case

11.7 流状态恢复的终极防御(应对交互式/脏数据输入)

说明: 面对可能包含非数字的脏数据,必须同时处理 faileof 状态。clear 恢复流状态,ignore 跳过错误行。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>
#include<vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::vector<int> nums;
    int x;
    int targetCount = 3;    // 目标读取 3 个有效整数

    while (targetCount > 0)
    {
        if (std::cin >> x)
        {
            // 读取成功
            nums.push_back(x);
            --targetCount;
        }
        else
        {
            // 检查是否提前遇到 EOF
            if (std::cin.eof())
            {
                break;
            }
            // 清除 fail 状态,跳过当前行剩余内容
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
    }

    // 输出验证
    std::cout << "最终有效数据:";
    for (std::size_t i = 0; i < nums.size(); ++i) 
    {
        std::cout << ' ' << nums[i];
    }
    std::cout << '\n';

    return 0;
}
plaintext 复制代码
abc
10
xyz
20
30

11.8 EOF 边界安全的 ignore 封装

说明:cin >> 读完最后一个数字后直接到达 EOF(末尾无换行符)时,调用 ignore 会触发 failbit,导致后续 getline 失败。封装 safe_ignore,仅在流未处于 EOF 状态时执行忽略操作。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>
#include<vector>
#include<utility>

// 安全 ignore: 防止在 EOF 边界污染流状态
void safe_ignore(std::istream& is)
{
    if (!is.eof())
    {
        is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
}

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::vector<std::pair<int, std::string>> records;
    int id;

    while (std::cin >> id)
    {
        // 使用 safe_ignore 替代原生 ignore,防止 EOF 时触发 failbit
        safe_ignore(std::cin);
        
        std::string desc;
        if (std::getline(std::cin, desc))
        {
            if (!desc.empty() && desc.back() == '\r')
            {
                desc.pop_back();;
            }
            records.push_back({id, desc});
        }
        else
        {
            // 处理只有 id 没有描述的边界情况
            records.push_back({ id, "[NO_DESC]" });
        }
    }

    // 输出验证
    std::cout << "读取了 " << records.size() << " 条记录:\n";
    for (std::size_t i = 0; i < records.size(); ++i) {
        std::cout << "  id=" << records[i].first
            << ", desc=\"" << records[i].second << "\"\n";
    }

    return 0;
}
plaintext 复制代码
1 Apple
2 Banana
3

11.9 高性能行内解析(C++17 std::from_chars 替代 istringstream

说明: 在海量数据(10510^5105 行级别)下,istringstream 的构造开销可能导致 TLE。C++17 的 std::from_chars 是零分配、无异常、不依赖 locale 的高性能解析方案。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>
#include<charconv>    // C++ 17 高性能字符转换
#include<system_error>    // std::errc
#include<limits>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
    std::vector<std::vector<int>> matrix(n);
    for (int i = 0; i < n; i++)
    {
        std::string line;
        std::getline(std::cin, line);
        if (!line.empty() && line.back() == '\r')
            line.pop_back();

        const char* ptr = line.data();
        const char* end = ptr + line.size();

        while (ptr < end)
        {
            // 手动调过空白符(from_chars 不会自动跳过)
            while (ptr < end && (*ptr == ' ' || *ptr == '\t')) ptr++;
            if (ptr >= end) break;

            int val = 0;
            // from_chars 解析整数,返回解析结果和下一个位置
            auto [next_ptr, ec] = std::from_chars(ptr, end, val);

            if (ec == std::errc{})
            {
                matrix[i].push_back(val);    // 解析成功
                ptr = next_ptr;
            }
            else
            {
                ptr++;    // 跳过无法解析的字符
            }
        }
    }

    // 输出验证
    std::cout << "高性能解析结果 (" << n << " 行):\n";
    for (int i = 0; i < n; ++i) 
    {
        std::cout << "  row[" << i << "] (" << matrix[i].size() << " 个):";
        for (std::size_t j = 0; j < matrix[i].size(); ++j) 
        {
            std::cout << ' ' << matrix[i][j];
        }
        std::cout << '\n';
    }

    return 0;
}
plaintext 复制代码
3
10 20 30 40
  5   6   7
100 200 300 400 500

11.10 noskipws 标志的 RAII 安全切换与恢复

说明: noskipws 会永久改变 cin 的格式标志,若忘记恢复会导致后续 cin >> 不再跳过前导空白。使用 RAII 守卫自动保存和恢复流标志。

cpp 复制代码
#include<iostream>
#include<string>
#include<vector>
#include<iomanip>

// RAII 风格的流标志保存与恢复器
class IosFlagSaver
{
public:
    explicit IosFlagSaver(std::ios& stream)
        : stream_(stream), saved_flags_(stream.flags()) { }

    ~IosFlagSaver() { stream_.flags(saved_flags_); }    // 析构时自动恢复原始标志

    IosFlagSaver(const IosFlagSaver&) = delete;
    IosFlagSaver& operator=(const IosFlagSaver&) = delete;

private:
    std::ios& stream_;
    std::ios::fmtflags saved_flags_;
};

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::string raw_data;
    {
        // 进入局部作用域,创建 RAII 守卫
        IosFlagSaver guard(std::cin);
        std::cin >> std::noskipws;    // 关闭跳过空白

        // 逐字符读取(包括空格和换行),直到读满n个字符
        for (int i = 0; i < n; i++)
        {
            char ch;
            if (std::cin >> ch)
            {
                raw_data += ch;
            }
        }
        // 离开作用域,guard析构,cin 的 skipws 标志被自动恢复
    }

    // 验证标志已恢复:cin >> 会正常跳过前导空白
    int trailing_num;
    if (std::cin >> trailing_num)
    {
        std::cout << "成功读取尾部数字: " << trailing_num << '\n';
    }

    // 输出验证
    std::cout << "原始字符数据 (长度 " << raw_data.size() << "):\n";
    std::cout << "  \"" << raw_data << "\"\n";

    std::cout << "  Hex dump:";
    for (char c : raw_data) 
    {
        std::cout << " 0x" << std::setw(2) << std::setfill('0')
            << std::hex << (int)(unsigned char)c;
    }
    std::cout << std::dec << '\n';

    return 0;
}
plaintext 复制代码
12
A B C D E F
999

11.11 全局 CRLF 防御型 getline 及复杂混合输入范式

说明: 封装 ultimate_getlineultimate_ignore,同时处理 CRLF 残留和 EOF 边界,适用于最复杂的混合输入场景。

cpp 复制代码
#include<iostream>
#include<string>
#include<limits>
#include<vector>
#include<utility>

// 防御型 getline: 自动处理 CRLF 和空行
std::istream& ultimate_getline(std::istream& is, std::string& t)
{
    std::getline(is, t);
    if (!t.empty() && t.back() == '\r')
    {
        t.pop_back();
    }
    return is;
}

// 防御型 ignore: 结合 EOF 检查
void ultimate_ignore(std::istream& is)
{
    if (!is.eof())
    {
        is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
}

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int T;
    std::cin >> T;
    ultimate_ignore(std::cin);    // 消除 T 后的换行

    for (int t = 1; t <= T; t++)
    {
        // 1. 读取用例标题(可能含空格)
        std::string title;
        ultimate_getline(std::cin, title);

        // 2. 读取 N 和 K
        int n, k;
        std::cin >> n >> k;
        ultimate_ignore(std::cin);    // 消除 N 和 K 后的换行

        // 3. 读取 N 个整数
        std::vector<int> a(n);
        for (int i = 0; i < n; i++)
        {
            std::cin >> a[i];
        }
        ultimate_ignore(std::cin);    // 消除数组行末尾的换行

        // 4. 读取一段自由文本描述(可能为空行)
        std::string description;
        ultimate_getline(std::cin, description);

        // 输出验证
        std::cout << "=== Case " << t << " ===\n";
        std::cout << "Tile: \"" << title << "\" (len=" << title.size() << ")\n";
        std::cout << "n=" << n << ", k=" << k << '\n';
        std::cout << "Array:";
        for (int i = 0; i < n; i++) std::cout << ' ' << a[i];
        std::cout << '\n';
        std::cout << "Desc: \"" << description << "\" (len=" << description.size() << ")\n";
    }

    return 0;
}
plaintext 复制代码
2
Graph Theory: Shortest Path
3 2
10 20 30
This is a description with spaces.
String Query: Palindrome
4 1
5 6 7 8
相关推荐
稚南城才子,乌衣巷风流15 小时前
函数:编程中的核心概念
开发语言·前端·javascript
皓月斯语17 小时前
【C++基础】三目运算符
开发语言·数据结构·c++
zhangrelay17 小时前
笔记本轻量高品质延寿工具完整分系统清单
运维·笔记·学习
巴巴媛66617 小时前
STM32学习笔记【38.DHT11实验】
笔记·stm32·学习
初願致夕霞17 小时前
C++懒汉单例设计详解
服务器·开发语言·c++
脱胎换骨-军哥17 小时前
C++分布式系统设计:从通信引擎到分布式共识
开发语言·c++·分布式
caimouse18 小时前
MM 学习笔记 01:miarm.h 核心头文件分析
笔记·学习·reactos
脱胎换骨-军哥18 小时前
C++零成本抽象理论深度拆解:现代C++如何在不牺牲性能的前提下提供高级语法封装
java·开发语言·c++
lbb 小魔仙18 小时前
VS Code Python 高级调试技巧:从入门到精通
开发语言·python