1.1基础知识:
在C++中,输入输出(IO)流是通过标准库中的 <iostream>
头文件来处理的。C++ 提供了几种基本的输入输出流类,最常用的有以下几种:
std::cin
:用于输入。std::cout
:用于输出。std::cerr
:用于输出错误信息(无缓冲)。std::clog
:用于输出日志信息(缓冲)。
基本使用示例
下面是一个简单的例子,展示如何使用这些 IO 流进行输入输出操作。
cpp
#include <iostream>
#include <string>
int main() {
std::string name;
int age;
// 输出提示信息
std::cout << "请输入您的名字: ";
std::cin >> name; // 从标准输入读取名字
std::cout << "请输入您的年龄: ";
std::cin >> age; // 从标准输入读取年龄
// 输出结果
std::cout << "你好, " << name << "!你今年 " << age << " 岁。" << std::endl;
return 0;
}
详细说明
-
输入 : 使用
std::cin
时,可以用>>
运算符来读取不同类型的数据。默认情况下,它会忽略空白字符(空格、换行符等),直到遇到下一个有效的输入。 -
输出 : 使用
std::cout
进行输出时,可以用<<
运算符将多个数据连接在一起,可以很方便地输出多个变量及文本。 -
错误输出 :
std::cerr
和std::clog
都用于输出错误信息,区别在于std::cerr
是无缓冲的,意味着内容会立即输出,而std::clog
是有缓冲的,可能会延迟输出。
注意事项
- 输入输出通常是在控制台进行的,要保证控制台能够接收和显示中文字符,可能需要设置locale。
- 使用
std::endl
时,它会在输出流中插入一个换行符,并刷新输出缓冲区。如果只需要换行,可以使用\n
,这样会提高效率,因为不一定每次都需要刷新。
1.2高级特性:
自定义输出格式
C++ 提供了多种方式来控制输出格式。你可以使用格式化标志和流操作符来改变输出格式。
cpp
#include <iostream>
#include <iomanip> // 用于 std::setprecision 和 std::fixed
int main() {
double number = 123.456789;
std::cout << std::fixed << std::setprecision(2) << number << std::endl; // 输出 123.46
return 0;
}
流状态管理
C++ 允许你检查和管理流的状态。你可以检查流是否处于有效状态,是否发生了错误,是否到达文件结束等。
cpp
#include <iostream>
#include <fstream>
int main() {
std::ifstream inFile("test.txt");
if (!inFile) {
std::cerr << "无法打开文件。" << std::endl;
return 1;
}
std::string line;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
if (inFile.eof()) {
std::cout << "到达文件结束。" << std::endl;
}
if (inFile.fail()) {
std::cout << "读取时发生错误。" << std::endl;
}
inFile.close();
return 0;
}
流操作符的重载
你可以通过重载 <<
和 >>
运算符来自定义对象的输入输出。
cpp
#include <iostream>
#include <string>
class Person {
public:
std::string name;
int age;
// 重载 << 运算符
friend std::ostream& operator<<(std::ostream& os, const Person& p) {
return os << p.name << " " << p.age;
}
// 重载 >> 运算符
friend std::istream& operator>>(std::istream& is, Person& p) {
return is >> p.name >> p.age;
}
};
int main() {
Person p{"Alice", 30};
std::cout << p << std::endl; // 输出:Alice 30
Person p2;
std::cin >> p2; // 输入格式:<名字> <年龄>
std::cout << p2 << std::endl;
return 0;
}
文件流异常处理
通过设置文件流的异常标志,可以更好地管理文件操作中的错误。
cpp
#include <iostream>
#include <fstream>
int main() {
std::ifstream inFile;
inFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); // 设置异常标志
try {
inFile.open("nonexistent.txt"); // 尝试打开不存在的文件
} catch (const std::ifstream::failure& e) {
std::cerr << "异常: " << e.what() << std::endl;
}
return 0;
}
使用字符串流
C++ 还提供了 std::ostringstream
和 std::istringstream
类,可以在内存中进行字符串流操作。
cpp
#include <iostream>
#include <sstream>
int main() {
std::ostringstream oss; // 输出字符串流
oss << "Hello, " << "World! " << 2023;
std::string str = oss.str(); // 获取字符串
std::cout << str << std::endl; // 输出:Hello, World! 2023
std::istringstream iss(str); // 输入字符串流
std::string word;
while (iss >> word) { // 从字符串中读取单词
std::cout << word << std::endl;
}
return 0;
}
文件缓冲管理
可以使用 std::ifstream
和 std::ofstream
的成员函数 rdbuf()
,进行流缓冲区的操作。例如,你可以直接操作底层缓冲区,或者在特定情况下改变缓冲方式。
cpp
#include <iostream>
#include <fstream>
int main() {
std::ofstream outFile("example.txt");
// 手动设置缓冲区
std::streambuf* originalBuf = std::cout.rdbuf(outFile.rdbuf());
std::cout << "这行将写入文件 example.txt" << std::endl;
// 恢复原来的缓冲区
std::cout.rdbuf(originalBuf);
std::cout << "这行将写入控制台" << std::endl;
return 0;
}
使用标准库算法与流结合
可以将标准库算法与输入输出流结合使用,例如.sort()
, std::copy()
等,可以更加高效地处理数据。
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
std::vector<int> numbers = {5, 3, 4, 1, 2};
std::sort(numbers.begin(), numbers.end());
std::cout << "排序后的数字: ";
std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
return 0;
}
2.1文件操作:
在C++中,除了标准输入输出流(如std::cin
和std::cout
)外,文件输入输出也是常用的功能。可以通过标准库中的 <fstream>
头文件来处理文件的读取和写入。
文件操作基础
C++ 提供了以下几个主要的文件流类:
std::ifstream
:输入文件流,用于从文件中读取数据。std::ofstream
:输出文件流,用于向文件中写入数据。std::fstream
:文件流,可以同时用于读取和写入操作。
基本使用示例
以下是一个简单的示例,展示如何使用这几个类进行文件操作。
写入文件:
cpp
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ofstream outFile("output.txt"); // 创建输出文件流
if (!outFile) { // 检查文件是否成功打开
std::cerr << "无法打开文件进行写入。" << std::endl;
return 1;
}
outFile << "这是一行文本。\n";
outFile << "这是文件中的第二行。" << std::endl;
outFile.close(); // 关闭文件流
return 0;
}
读取文件:
cpp
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream inFile("output.txt"); // 创建输入文件流
if (!inFile) { // 检查文件是否成功打开
std::cerr << "无法打开文件进行读取。" << std::endl;
return 1;
}
std::string line;
while (std::getline(inFile, line)) { // 逐行读取文件
std::cout << line << std::endl; // 输出到控制台
}
inFile.close(); // 关闭文件流
return 0;
}
2.2高级特性:
文件打开模式:
可以指定打开文件的方式,如只读、只写、追加等。可以通过组合打开模式标志来实现,例如:
cpp
std::ofstream outFile("output.txt", std::ios::app); // 追加模式
常用的模式标志有:
std::ios::in
:打开文件用于读取。std::ios::out
:打开文件用于写入。std::ios::app
:以追加方式打开文件。std::ios::trunc
:打开文件时,如果文件存在,则先清空文件。std::ios::binary
:以二进制模式打开文件。
异常处理:
可以使用异常机制来处理文件操作中的错误。通过在文件流中设置异常标志:
cpp
std::ifstream inFile("somefile.txt");
inFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
这将使得在文件打开或读取失败时抛出异常。
二进制文件的读写:
使用 std::ios::binary
打开文件,可以进行二进制数据的读写操作。例如,写入和读取基本数据类型或对象(如结构体)。
对象序列化:
可以通过重载 <<
和 >>
运算符来实现自定义对象的输入输出。
cpp
class Person {
public:
std::string name;
int age;
friend std::ostream& operator<<(std::ostream& os, const Person& p) {
os << p.name << " " << p.age;
return os;
}
friend std::istream& operator>>(std::istream& is, Person& p) {
is >> p.name >> p.age;
return is;
}
};
3.0模拟实现:
模拟目标:
我们将创建一个名为 MyStream
的类,模拟 C++ 的输入输出流。这个类将支持字符串的读取和写入,并维护一个内部缓冲区。
代码示例:
cpp
#include <iostream>
#include <string>
#include <sstream>
class MyStream {
private:
std::string buffer; // 内部缓冲区
size_t position; // 当前读取位置
public:
MyStream() : position(0) {}
// 写入字符串到缓冲区
void write(const std::string& str) {
buffer += str; // 追加字符串到缓冲区
}
// 从缓冲区读取字符串
bool read(std::string& outStr) {
if (position >= buffer.size()) {
return false; // 没有更多数据可读
}
// 查找下一个空格
size_t nextSpace = buffer.find(' ', position);
if (nextSpace == std::string::npos) {
nextSpace = buffer.size(); // 到达字符串末尾
}
// 提取子字符串
outStr = buffer.substr(position, nextSpace - position);
position = nextSpace + 1; // 更新当前位置
return true; // 成功读取
}
// 清空缓冲区
void clear() {
buffer.clear();
position = 0;
}
// 打印当前缓冲区的内容
void print() const {
std::cout << "Buffer: " << buffer << ", Position: " << position << std::endl;
}
};
int main() {
MyStream myStream;
// 写入数据到自定义流
myStream.write("Hello World from MyStream");
myStream.print();
// 读取数据
std::string word;
while (myStream.read(word)) {
std::cout << "Read: " << word << std::endl;
}
// 再次写入新的数据
myStream.write("Another line");
myStream.print();
// 在读取新数据之前清空流
myStream.clear();
myStream.print();
// 再次写入数据
myStream.write("After clearing the buffer");
myStream.print();
// 读取新数据
while (myStream.read(word)) {
std::cout << "Read: " << word << std::endl;
}
return 0;
}
MyStream
类:
buffer
用于保存写入的数据。position
用于记录当前读取的位置。write
方法用于向缓冲区添加字符串。read
方法用于从缓冲区读取下一个词并更新读取位置。如果没有可读的数据,返回false
。clear
方法用于清空缓冲区和重置位置。print
方法用于打印当前的缓冲区内容和位置。
main
函数:
- 创建
MyStream
对象并写入示例字符串。 - 读取缓冲区中的单词并打印。
- 清空缓冲区后再次写入并读取。
尽管这只是一个基础示例,但它揭示了流机制的基本概念,如缓冲、读取位置和字符串处理。
感谢大家!