C++ 流迭代器
在C++的STL(标准模板库)中,流迭代器(Stream Iterators)是连接容器与输入/输出流的桥梁。通过流迭代器,我们可以像操作容器中的元素一样轻松地处理输入输出流中的数据,大大简化了程序的编写。
流迭代器的基本概念
流迭代器主要分为两类:
- 输入流迭代器(istream_iterator):从输入流(如
cin
)读取数据 - 输出流迭代器(ostream_iterator):向输出流(如
cout
)写入数据
这两种迭代器让我们能够像处理容器数据一样处理输入输出流,使得我们可以将STL算法直接应用于输入输出操作。
输入流迭代器(istream_iterator)
输入流迭代器允许我们从输入流中读取数据,就像从容器中读取元素一样。
基本语法
istream_iterator<T> it(istream& is); // 创建一个输入流迭代器,从is流中读取T类型的数据
istream_iterator<T> eof; // 创建一个表示结束的迭代器
简单示例
下面是一个使用输入流迭代器从标准输入读取整数的简单示例:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
int main() {
std::cout << "请输入一些整数(Ctrl+Z结束输入):";
// 创建一个从标准输入读取整数的迭代器
std::istream_iterator<int> input_iterator(std::cin);
// 创建一个表示结束的迭代器
std::istream_iterator<int> eos;
// 使用输入迭代器构建vector
std::vector<int> numbers(input_iterator, eos);
std::cout << "您输入的数字有:";
for (const auto& num : numbers) {
std::cout << num << " ";
}
std::cout << "\n总共: " << numbers.size() << " 个数字" << std::endl;
return 0;
}
输入示例:
1 2 3 4 5^Z
输出结果:
请输入一些整数(Ctrl+Z结束输入):1 2 3 4 5^Z
您输入的数字有:1 2 3 4 5
总共: 5 个数字
在Windows系统中,使用Ctrl+Z表示输入结束;而在Unix/Linux系统中,使用Ctrl+D。
输出流迭代器(ostream_iterator)
输出流迭代器允许我们向输出流写入数据,就像向容器中添加元素一样。
基本语法
ostream_iterator<T> it(ostream& os); // 创建一个输出流迭代器,向os流中写入T类型的数据
ostream_iterator<T> it(ostream& os, const char* delim); // 创建一个输出流迭代器,每次写入后添加delim分隔符
简单示例
下面是一个使用输出流迭代器向标准输出写入数据的示例:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {10, 20, 30, 40, 50};
// 创建一个向标准输出写入整数的迭代器,每个数字后面跟一个空格
std::ostream_iterator<int> output_iterator(std::cout, " ");
std::cout << "使用ostream_iterator输出vector中的内容:" << std::endl;
// 使用copy算法和输出迭代器输出vector中的内容
std::copy(numbers.begin(), numbers.end(), output_iterator);
std::cout << std::endl;
return 0;
}
输出结果:
使用ostream_iterator输出vector中的内容:
10 20 30 40 50
流迭代器的高级用法
结合STL算法使用
流迭代器的强大之处在于它可以与STL算法无缝集成,下面是一些常见的组合:
1. 使用transform算法处理输入并输出
#include <iostream>
#include <iterator>
#include <algorithm>
#include <functional>
int main() {
std::cout << "请输入一些整数(Ctrl+Z结束输入):";
// 从标准输入读取整数
std::istream_iterator<int> input(std::cin);
std::istream_iterator<int> eos;
// 向标准输出写入整数(每个数字后跟一个空格)
std::ostream_iterator<int> output(std::cout, " ");
// 使用transform算法将每个输入的数字平方后输出
std::cout << "这些数字的平方是:";
std::transform(input, eos, output, [](int x) { return x * x; });
std::cout << std::endl;
return 0;
}
输入示例:
1 2 3 4 5^Z
输出结果:
请输入一些整数(Ctrl+Z结束输入):1 2 3 4 5^Z
这些数字的平方是:1 4 9 16 25
2. 使用copy_if算法进行过滤
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << "原始数字:";
std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::cout << "偶数:";
// 只复制偶数到输出迭代器
std::copy_if(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, " "),
[](int x) { return x % 2 == 0; });
std::cout << std::endl;
return 0;
}
输出结果:
原始数字:1 2 3 4 5 6 7 8 9 10
偶数:2 4 6 8 10
从文件读取和写入
流迭代器也可以用于文件输入输出:
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <algorithm>
int main() {
// 从文件读取数据
std::ifstream input_file("input.txt");
if (!input_file) {
std::cerr << "无法打开输入文件!" << std::endl;
return 1;
}
// 创建输入流迭代器
std::istream_iterator<int> file_it(input_file);
std::istream_iterator<int> eos;
// 读取数据到vector
std::vector<int> numbers(file_it, eos);
// 处理数据(每个数乘以2)
std::transform(numbers.begin(), numbers.end(), numbers.begin(),
[](int x) { return x * 2; });
// 创建输出文件
std::ofstream output_file("output.txt");
if (!output_file) {
std::cerr << "无法创建输出文件!" << std::endl;
return 1;
}
// 创建输出流迭代器,每个数字后添加换行符
std::ostream_iterator<int> file_out(output_file, "\n");
// 写入处理后的数据
std::copy(numbers.begin(), numbers.end(), file_out);
std::cout << "成功处理了 " << numbers.size() << " 个数字。" << std::endl;
return 0;
}
使用流迭代器处理文件时,记得检查文件是否成功打开,并在程序结束时关闭文件(虽然这里依赖于RAII自动关闭)。
实际应用案例
案例1: 简单的数据过滤与转换工具
假设你需要开发一个小工具,从用户输入中过滤出正整数并计算它们的平均值:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <numeric>
int main() {
std::cout << "请输入一系列整数(可以是负数),Ctrl+Z结束:" << std::endl;
// 从标准输入读取整数
std::istream_iterator<int> input(std::cin);
std::istream_iterator<int> end;
// 存储所有输入的数字
std::vector<int> all_numbers(input, end);
// 只保留正数
std::vector<int> positive_numbers;
std::copy_if(all_numbers.begin(), all_numbers.end(),
std::back_inserter(positive_numbers),
[](int x) { return x > 0; });
std::cout << "输入的正整数有:";
std::copy(positive_numbers.begin(), positive_numbers.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
// 计算平均值
if (!positive_numbers.empty()) {
double average = static_cast<double>(std::accumulate(positive_numbers.begin(),
positive_numbers.end(), 0))
/ positive_numbers.size();
std::cout << "这些正整数的平均值为:" << average << std::endl;
}
else {
std::cout << "没有输入正整数!" << std::endl;
}
return 0;
}
输入示例:
-5 10 -3 8 15 -7 2^Z
输出结果:
请输入一系列整数(可以是负数),Ctrl+Z结束:
-5 10 -3 8 15 -7 2^Z
输入的正整数有:10 8 15 2
这些正整数的平均值为:8.75
案例2: 简易CSV文件解析器
假设你需要解析一个简单的CSV文件,每行包含一个名称和一个分数:
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
struct Student {
std::string name;
int score;
friend std::istream& operator>>(std::istream& is, Student& student) {
std::string line;
if (std::getline(is, line)) {
std::stringstream ss(line);
std::getline(ss, student.name, ',');
ss >> student.score;
}
return is;
}
friend std::ostream& operator<<(std::ostream& os, const Student& student) {
os << student.name << " 得分: " << student.score;
return os;
}
};
int main() {
// 模拟一个CSV文件
std::istringstream csv_data(
"张三,85\n"
"李四,92\n"
"王五,78\n"
"赵六,96\n"
);
// 使用流迭代器读取学生数据
std::istream_iterator<Student> file_it(csv_data);
std::istream_iterator<Student> end_it;
// 存储所有学生数据
std::vector<Student> students(file_it, end_it);
// 输出学生信息
std::cout << "学生名单:" << std::endl;
std::copy(students.begin(), students.end(),
std::ostream_iterator<Student>(std::cout, "\n"));
// 找出分数最高的学生
auto max_student = std::max_element(students.begin(), students.end(),
[](const Student& a, const Student& b) {
return a.score < b.score;
});
std::cout << "\n分数最高的学生是:" << *max_student << std::endl;
return 0;
}
输出结果:
学生名单:
张三 得分: 85
李四 得分: 92
王五 得分: 78
赵六 得分: 96
分数最高的学生是:赵六 得分: 96
为了使用自定义类型(如上例中的Student)与流迭代器一起工作,你必须为该类型定义适当的输入操作符(>>
)和输出操作符(<<
)。
总结
C++流迭代器是STL中非常实用的工具,它们提供了一种统一的方式来处理内存中的数据和输入输出流。通过流迭代器,我们可以:
- 使用与容器相同的接口处理输入输出操作
- 将STL算法直接应用于输入输出流
- 简化文件处理和数据转换代码
- 编写更加简洁、可读的代码
掌握流迭代器是提高C++编程效率的重要一步,特别是当你处理大量数据输入输出时,流迭代器能让你的代码更加简洁、高效。
练习
为了加深对流迭代器的理解,你可以尝试以下练习:
-
编写一个程序,使用输入流迭代器从用户输入读取一系列单词,然后使用输出流迭代器按字母表顺序输出这些单词。
-
修改上面的案例2,使其从实际的CSV文件中读取数据,并计算所有学生的平均分。
-
编写一个简单的程序,从一个文件读取整数,将它们转换为浮点数(乘以1.5),然后将结果写入另一个文件。
-
创建一个程序,使用流迭代器统计文本文件中每个单词出现的次数,并按频率从高到低输出结果。
扩展资源
- C++ 参考 - istream_iterator
- C++ 参考 - ostream_iterator
- C++ Primer(第5版)中的流迭代器相关章节
- Effective STL by Scott Meyers(项目23:考虑用流迭代器替代手工编写的循环)
通过这些资源和练习,你将能够更深入地理解并熟练使用C++流迭代器,从而编写更加高效、简洁的代码。