C++ IO 与网络编程总结
一、C++ IO 流体系
⭐ IO 流继承体系
⭐ IO 流分类
| 分类 | 输入类 | 输出类 | 读写类 | 用途 |
|---|---|---|---|---|
| 标准流 | cin | cout / cerr / clog | — | 控制台 IO |
| 文件流 | ifstream | ofstream | fstream | 文件 IO |
| 字符串流 | istringstream | ostringstream | stringstream | 内存中字符串处理 |
二、文件 IO
⭐ 文件读写的基本操作
cpp
#include <fstream>
#include <string>
// 写文件
void WriteFile(const std::string& path) {
std::ofstream ofs(path); // RAII:构造时打开
if (!ofs.is_open()) {
throw std::runtime_error("Cannot open file: " + path);
}
ofs << "Hello, World!\n";
ofs << "Line 2\n";
// 析构时自动关闭(RAII)
}
// 读文件(逐行)
void ReadFile(const std::string& path) {
std::ifstream ifs(path);
if (!ifs) return; // operator bool() 检查状态
std::string line;
while (std::getline(ifs, line)) {
std::cout << line << "\n";
}
}
// 读取整个文件到字符串
std::string ReadAll(const std::string& path) {
std::ifstream ifs(path);
return std::string(
std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>()
);
}
⭐ 文件打开模式
| 模式 | 含义 | 说明 |
|---|---|---|
ios::in | 读取 | ifstream 默认 |
ios::out | 写入(覆盖) | ofstream 默认 |
ios::app | 追加 | 写入到文件末尾 |
ios::ate | 打开后定位到末尾 | 可以修改位置 |
ios::binary | 二进制模式 | 不转换换行符 |
ios::trunc | 截断(清空) | 与 out 配合 |
cpp
// 追加模式
std::ofstream ofs("log.txt", std::ios::app);
// 二进制读写
std::ofstream bin("data.bin", std::ios::binary);
int data = 42;
bin.write(reinterpret_cast<const char*>(&data), sizeof(data));
std::ifstream bin_in("data.bin", std::ios::binary);
int result;
bin_in.read(reinterpret_cast<char*>(&result), sizeof(result));
⭐ 流状态检查
cpp
std::ifstream ifs("data.txt");
int x;
while (ifs >> x) { // 读取成功则继续
std::cout << x << "\n";
}
if (ifs.eof()) {
std::cout << "Reached end of file\n";
} else if (ifs.fail()) {
std::cout << "Format error (not an int?)\n";
ifs.clear(); // 清除错误状态
ifs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
} else if (ifs.bad()) {
std::cout << "Critical IO error\n";
}
三、字符串流
⭐ stringstream 的典型用法
cpp
#include <sstream>
// 1. 类型转换(数字 ↔ 字符串)
int num = 42;
std::ostringstream oss;
oss << num;
std::string s = oss.str(); // "42"
// 更简洁:std::to_string (C++11)
std::string s2 = std::to_string(42);
int n = std::stoi("42");
// 2. 字符串拼接
std::ostringstream builder;
builder << "Name: " << name << ", Age: " << age;
std::string result = builder.str();
// 3. 字符串分割
std::string input = "hello world foo bar";
std::istringstream iss(input);
std::string word;
std::vector<std::string> words;
while (iss >> word) {
words.push_back(word);
}
// 4. 按分隔符分割
std::string csv = "a,b,c,d";
std::istringstream ss(csv);
std::string token;
while (std::getline(ss, token, ',')) {
std::cout << token << "\n";
}
// 5. 格式化输出
std::ostringstream fmt;
fmt << std::fixed << std::setprecision(2) << 3.14159;
std::string pi = fmt.str(); // "3.14"
C++20 std::format
cpp
#include <format>
// C++20 格式化(类似 Python 的 f-string)
std::string s = std::format("Name: {}, Age: {}", "Alice", 30);
std::string hex = std::format("{:#x}", 255); // "0xff"
std::string pi = std::format("{:.2f}", 3.14159); // "3.14"
std::string pad = std::format("{:>10}", "hello"); // " hello"
四、C++ 网络编程基础
⭐ TCP/IP 通信模型
TCP 三次握手与四次挥手
⭐ TCP Socket 编程示例
cpp
// === 服务端 ===
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
void TcpServer(int port) {
// 1. 创建 socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 绑定地址
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
bind(server_fd, (sockaddr*)&addr, sizeof(addr));
// 3. 监听
listen(server_fd, 5);
// 4. 接受连接
sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (sockaddr*)&client_addr, &client_len);
// 5. 收发数据
char buf[1024]{};
ssize_t n = recv(client_fd, buf, sizeof(buf), 0);
send(client_fd, "OK", 2, 0);
// 6. 关闭
close(client_fd);
close(server_fd);
}
// === 客户端 ===
void TcpClient(const char* ip, int port) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, ip, &addr.sin_addr);
connect(fd, (sockaddr*)&addr, sizeof(addr));
send(fd, "Hello", 5, 0);
char buf[1024]{};
recv(fd, buf, sizeof(buf), 0);
close(fd);
}
五、IO 多路复用
⭐ select / poll / epoll 对比
| 特性 | select | poll | epoll |
|---|---|---|---|
| fd 上限 | 1024 | 无限制 | 无限制 |
| fd 传递 | 每次拷贝到内核 | 每次拷贝到内核 | 内核维护,无需拷贝 |
| 触发方式 | 水平触发 | 水平触发 | 水平 + 边缘触发 |
| 就绪检测 | 遍历全部 O(n) | 遍历全部 O(n) | 回调通知 O(1) |
| 适用场景 | 连接数少 | 连接数中等 | 高并发(推荐) |
epoll 的基本用法
cpp
#include <sys/epoll.h>
void EpollExample() {
// 1. 创建 epoll 实例
int epfd = epoll_create1(0);
// 2. 注册监听事件
epoll_event ev{};
ev.events = EPOLLIN | EPOLLET; // 读事件 + 边缘触发
ev.data.fd = server_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev);
// 3. 等待事件
constexpr int MAX_EVENTS = 64;
epoll_event events[MAX_EVENTS];
while (true) {
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; ++i) {
if (events[i].data.fd == server_fd) {
// 新连接到来
int client = accept(server_fd, nullptr, nullptr);
// 注册客户端 fd ...
} else {
// 数据可读
HandleClient(events[i].data.fd);
}
}
}
}
六、Reactor 模式
⭐ 单 Reactor 单线程模型
⭐ 主从 Reactor 多线程模型
经典实现:Nginx(多进程 Reactor)、Netty(主从 Reactor)、muduo(one loop per thread)
七、C++17/20 文件系统
std::filesystem(C++17)
cpp
#include <filesystem>
namespace fs = std::filesystem;
// 路径操作
fs::path p = "/home/user/data.txt";
p.filename(); // "data.txt"
p.stem(); // "data"
p.extension(); // ".txt"
p.parent_path(); // "/home/user"
// 文件操作
bool exists = fs::exists(p);
auto size = fs::file_size(p);
fs::copy("src.txt", "dst.txt");
fs::remove("temp.txt");
fs::rename("old.txt", "new.txt");
// 目录遍历
for (const auto& entry : fs::directory_iterator("/home/user")) {
std::cout << entry.path() << "\n";
}
// 递归遍历
for (const auto& entry : fs::recursive_directory_iterator("/project")) {
if (entry.path().extension() == ".cpp") {
std::cout << entry.path() << "\n";
}
}
// 创建目录
fs::create_directories("/path/to/dir");
八、总结知识图谱
📑 文章目录
💬 评论