C++ Primer 第17章:标准库特殊设施
17.1 tuple 类型
17.1.1 tuple 基础
tuple 是 pair 的泛化: pair → 两个成员 tuple → 任意数量的成员,每个成员可以是不同类型 头文件:<tuple>
// tuple_basic.cpp -- tuple基础 #include <iostream> #include <tuple> #include <string> #include <vector> int main() { using namespace std; // ===== 创建 tuple ===== tuple<int, string, double> t1(42, "Hello", 3.14); auto t2 = make_tuple(42, "Hello", 3.14); // 推断类型 // C++17 结构化绑定 auto [i, s, d] = t2; cout << i << " " << s << " " << d << endl; // ===== 访问 tuple 成员 ===== // get<N>(t):获取第N个成员(从0开始) cout << get<0>(t1) << endl; // 42 cout << get<1>(t1) << endl; // Hello cout << get<2>(t1) << endl; // 3.14 // 修改成员 get<0>(t1) = 100; cout << "修改后:" << get<0>(t1) << endl; // ===== tuple 的大小 ===== cout << "成员数:" << tuple_size<decltype(t1)>::value << endl; // 3 // ===== tuple 的成员类型 ===== tuple_element<1, decltype(t1)>::type str = "World"; cout << str << endl; // ===== tuple 的比较 ===== auto ta = make_tuple(1, 2, 3); auto tb = make_tuple(1, 2, 4); cout << boolalpha; cout << "ta < tb: " << (ta < tb) << endl; // true cout << "ta == tb: " << (ta == tb) << endl; // false // ===== tuple 的实际应用:返回多个值 ===== auto getMinMax = [](const vector<int>& v) -> tuple<int, int, double> { int minV = *min_element(v.begin(), v.end()); int maxV = *max_element(v.begin(), v.end()); double avg = accumulate(v.begin(), v.end(), 0.0) / v.size(); return {minV, maxV, avg}; }; vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6}; auto [minV, maxV, avg] = getMinMax(nums); cout << "min=" << minV << " max=" << maxV << " avg=" << avg << endl; return 0; }
17.1.2 tuple 的高级用法
// tuple_advanced.cpp -- tuple高级用法 #include <iostream> #include <tuple> #include <string> #include <vector> #include <algorithm> // ===== 用 tuple 实现多键排序 ===== struct Employee { string name; int dept; double salary; }; int main() { using namespace std; vector<Employee> employees = { {"张三", 2, 8000}, {"李四", 1, 9000}, {"王五", 2, 7500}, {"赵六", 1, 8500}, {"钱七", 3, 10000} }; // 按部门升序,同部门按薪资降序排序 sort(employees.begin(), employees.end(), [](const Employee& a, const Employee& b) { return make_tuple(a.dept, -a.salary) < make_tuple(b.dept, -b.salary); }); cout << "排序结果:" << endl; for (const auto& e : employees) cout << " 部门" << e.dept << " " << e.name << " $" << e.salary << endl; // ===== tuple 拼接 ===== auto t1 = make_tuple(1, 2, 3); auto t2 = make_tuple("a", "b"); auto t3 = tuple_cat(t1, t2); // 拼接两个tuple cout << "\ntuple_cat结果:" << endl; cout << get<0>(t3) << " " << get<1>(t3) << " " << get<2>(t3) << " " << get<3>(t3) << " " << get<4>(t3) << endl; // ===== tie:解包 tuple ===== int a, b, c; tie(a, b, c) = make_tuple(10, 20, 30); cout << "\ntie解包:" << a << " " << b << " " << c << endl; // 忽略某些成员 tie(a, ignore, c) = make_tuple(100, 200, 300); cout << "忽略中间:" << a << " " << c << endl; return 0; }
17.2 bitset 类型
// bitset_demo.cpp -- bitset详解 #include <iostream> #include <bitset> #include <string> int main() { using namespace std; // ===== 创建 bitset ===== bitset<8> b1; // 8位,全0 bitset<8> b2(0b10110100); // 从整数初始化 bitset<8> b3("10110100"); // 从字符串初始化 bitset<8> b4(0xFF); // 255 = 11111111 cout << "b1 = " << b1 << endl; // 00000000 cout << "b2 = " << b2 << endl; // 10110100 cout << "b3 = " << b3 << endl; // 10110100 cout << "b4 = " << b4 << endl; // 11111111 // ===== 访问位 ===== cout << "\n访问位:" << endl; cout << "b2[0] = " << b2[0] << endl; // 最低位:0 cout << "b2[2] = " << b2[2] << endl; // 1 cout << "b2[7] = " << b2[7] << endl; // 最高位:1 // test():检查指定位(有边界检查) cout << "b2.test(2) = " << boolalpha << b2.test(2) << endl; // ===== 修改位 ===== bitset<8> b = b2; b.set(0); // 将位0置1 b.reset(7); // 将位7置0 b.flip(4); // 翻转位4 cout << "\n修改后:" << b << endl; b.set(); // 所有位置1 cout << "全1:" << b << endl; b.reset(); // 所有位置0 cout << "全0:" << b << endl; b.flip(); // 翻转所有位 cout << "翻转:" << b << endl; // ===== 位运算 ===== bitset<8> x("10110100"); bitset<8> y("01101100"); cout << "\n位运算:" << endl; cout << "x & y = " << (x & y) << endl; // 按位与 cout << "x | y = " << (x | y) << endl; // 按位或 cout << "x ^ y = " << (x ^ y) << endl; // 按位异或 cout << "~x = " << (~x) << endl; // 按位取反 cout << "x << 2 = " << (x << 2) << endl; // 左移 cout << "x >> 2 = " << (x >> 2) << endl; // 右移 // ===== 统计和转换 ===== cout << "\n统计:" << endl; cout << "count(1的个数) = " << x.count() << endl; cout << "size(总位数) = " << x.size() << endl; cout << "any(有1) = " << x.any() << endl; cout << "all(全1) = " << x.all() << endl; cout << "none(全0) = " << x.none() << endl; // 转换 cout << "\n转换:" << endl; cout << "to_ulong = " << x.to_ulong() << endl; cout << "to_ullong = " << x.to_ullong() << endl; cout << "to_string = " << x.to_string() << endl; // ===== 实际应用:权限管理 ===== cout << "\n===== 权限管理 =====" << endl; enum Permission { READ = 0, WRITE = 1, EXECUTE = 2, ADMIN = 3 }; bitset<4> userPerm; userPerm.set(READ); userPerm.set(WRITE); cout << "用户权限:" << userPerm << endl; cout << "有读权限:" << userPerm.test(READ) << endl; cout << "有执行权限:" << userPerm.test(EXECUTE) << endl; // 添加权限 userPerm.set(EXECUTE); cout << "添加执行权限后:" << userPerm << endl; // 删除权限 userPerm.reset(WRITE); cout << "删除写权限后:" << userPerm << endl; return 0; }
17.3 正则表达式
// regex_demo.cpp -- 正则表达式 #include <iostream> #include <regex> #include <string> #include <vector> int main() { using namespace std; // ===== 基本匹配 ===== cout << "===== 基本匹配 =====" << endl; string pattern = R"(\d{3}-\d{4}-\d{4})"; // 手机号格式 regex re(pattern); string text = "联系方式:138-1234-5678 或 010-12345678"; // regex_search:在字符串中搜索 smatch match; if (regex_search(text, match, re)) cout << "找到手机号:" << match[0] << endl; // regex_match:整个字符串匹配 string phone = "138-1234-5678"; cout << "是手机号:" << boolalpha << regex_match(phone, re) << endl; // ===== 查找所有匹配 ===== cout << "\n===== 查找所有匹配 =====" << endl; string emails = "联系 alice@example.com 或 bob@test.org 获取帮助"; regex emailRe(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); sregex_iterator it(emails.begin(), emails.end(), emailRe); sregex_iterator end; while (it != end) { cout << "邮箱:" << (*it)[0] << endl; ++it; } // ===== 捕获组 ===== cout << "\n===== 捕获组 =====" << endl; string date = "2024-01-15"; regex dateRe(R"((\d{4})-(\d{2})-(\d{2}))"); smatch dateMatch; if (regex_match(date, dateMatch, dateRe)) { cout << "完整匹配:" << dateMatch[0] << endl; cout << "年:" << dateMatch[1] << endl; cout << "月:" << dateMatch[2] << endl; cout << "日:" << dateMatch[3] << endl; } // ===== 替换 ===== cout << "\n===== 替换 =====" << endl; string text2 = "Hello World Hello C++"; regex helloRe("Hello"); // regex_replace:替换所有匹配 string result = regex_replace(text2, helloRe, "Hi"); cout << "替换后:" << result << endl; // 只替换第一个 string result2 = regex_replace(text2, helloRe, "Hi", regex_constants::format_first_only); cout << "只替换第一个:" << result2 << endl; // ===== 分割字符串 ===== cout << "\n===== 分割字符串 =====" << endl; string csv = "apple,banana,,cherry,date"; regex sepRe(","); sregex_token_iterator tokIt(csv.begin(), csv.end(), sepRe, -1); sregex_token_iterator tokEnd; while (tokIt != tokEnd) { cout << "\"" << *tokIt << "\"" << endl; ++tokIt; } // ===== 实际应用:验证输入 ===== cout << "\n===== 输入验证 =====" << endl; auto validate = [](const string& input, const string& pattern, const string& name) { regex re(pattern); bool valid = regex_match(input, re); cout << name << " \"" << input << "\":" << boolalpha << valid << endl; return valid; }; // 验证邮箱 validate("user@example.com", R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", "邮箱"); validate("invalid-email", R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", "邮箱"); // 验证IP地址 validate("192.168.1.1", R"((\d{1,3}\.){3}\d{1,3})", "IP地址"); validate("256.0.0.1", R"((\d{1,3}\.){3}\d{1,3})", "IP地址"); return 0; }
17.4 随机数
// random_demo.cpp -- 随机数 #include <iostream> #include <random> #include <vector> #include <map> #include <iomanip> #include <algorithm> int main() { using namespace std; // ===== 随机数引擎 ===== // 默认随机数引擎 default_random_engine engine; // 使用随机设备作为种子(真随机) random_device rd; default_random_engine engine2(rd()); // 使用时间作为种子 default_random_engine engine3(time(nullptr)); // ===== 均匀分布 ===== cout << "===== 均匀分布 =====" << endl; // 整数均匀分布 [1, 6](模拟骰子) uniform_int_distribution<int> dice(1, 6); cout << "骰子:"; for (int i = 0; i < 10; i++) cout << dice(engine2) << " "; cout << endl; // 浮点均匀分布 [0, 1) uniform_real_distribution<double> prob(0.0, 1.0); cout << "概率:"; for (int i = 0; i < 5; i++) cout << fixed << setprecision(3) << prob(engine2) << " "; cout << endl; // ===== 正态分布 ===== cout << "\n===== 正态分布 =====" << endl; normal_distribution<double> normal(0.0, 1.0); // 均值0,标准差1 // 统计分布 map<int, int> hist; for (int i = 0; i < 10000; i++) { int bucket = static_cast<int>(round(normal(engine2))); hist[bucket]++; } cout << "正态分布直方图:" << endl; for (auto& [val, cnt] : hist) { if (val >= -3 && val <= 3) { cout << setw(3) << val << " | "; cout << string(cnt / 100, '*') << endl; } } // ===== 其他分布 ===== cout << "\n===== 其他分布 =====" << endl; // 伯努利分布(成功概率0.7) bernoulli_distribution bern(0.7); int successes = 0; for (int i = 0; i < 1000; i++) if (bern(engine2)) successes++; cout << "伯努利(p=0.7),1000次成功:" << successes << endl; // 泊松分布(平均值4) poisson_distribution<int> poisson(4.0); cout << "泊松分布(λ=4):"; for (int i = 0; i < 10; i++) cout << poisson(engine2) << " "; cout << endl; // ===== 随机打乱 ===== cout << "\n===== 随机打乱 =====" << endl; vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; shuffle(v.begin(), v.end(), engine2); cout << "打乱后:"; for (int x : v) cout << x << " "; cout << endl; // ===== 实际应用:随机抽样 ===== cout << "\n===== 随机抽样 =====" << endl; vector<string> names = {"Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry"}; // 随机选3个 shuffle(names.begin(), names.end(), engine2); cout << "随机选3人:"; for (int i = 0; i < 3; i++) cout << names[i] << " "; cout << endl; return 0; }
17.5 IO 库再探
17.5.1 格式化输入输出
// io_format.cpp -- 格式化IO #include <iostream> #include <iomanip> #include <sstream> #include <string> int main() { using namespace std; // ===== 整数格式 ===== cout << "===== 整数格式 =====" << endl; int n = 255; cout << "十进制:" << dec << n << endl; cout << "八进制:" << oct << n << endl; cout << "十六进制:" << hex << n << endl; cout << "十六进制大写:" << uppercase << hex << n << endl; cout << dec << nouppercase; // 恢复 // 显示进制前缀 cout << showbase; cout << "带前缀十进制:" << dec << n << endl; cout << "带前缀八进制:" << oct << n << endl; cout << "带前缀十六进制:" << hex << n << endl; cout << noshowbase << dec; // 显示正号 cout << showpos << 42 << " " << -42 << noshowpos << endl; // ===== 浮点格式 ===== cout << "\n===== 浮点格式 =====" << endl; double pi = 3.14159265358979; cout << "默认:" << pi << endl; cout << fixed << "fixed:" << pi << endl; cout << scientific << "scientific:" << pi << endl; cout << defaultfloat; // 精度 for (int p = 1; p <= 8; p++) cout << "precision(" << p << "):" << setprecision(p) << pi << endl; cout << setprecision(6); // 恢复默认 // ===== 宽度和对齐 ===== cout << "\n===== 宽度对齐 =====" << endl; cout << setw(10) << "右对齐" << "|" << endl; cout << left << setw(10) << "左对齐" << "|" << endl; cout << right; // 填充字符 cout << setfill('0') << setw(8) << 42 << endl; // 00000042 cout << setfill('*') << setw(10) << "Hi" << endl; // ********Hi cout << setfill(' '); // 恢复 // ===== 格式化表格 ===== cout << "\n===== 格式化表格 =====" << endl; cout << fixed << setprecision(2); cout << left << setw(12) << "商品" << right << setw(8) << "数量" << setw(10) << "单价" << setw(10) << "小计" << endl; cout << string(40, '-') << endl; struct Item { string name; int qty; double price; }; Item items[] = {{"苹果", 5, 3.50}, {"香蕉", 12, 1.80}, {"橙子", 8, 4.20}}; double total = 0; for (const auto& item : items) { double subtotal = item.qty * item.price; total += subtotal; cout << left << setw(12) << item.name << right << setw(8) << item.qty << setw(10) << item.price << setw(10) << subtotal << endl; } cout << string(40, '-') << endl; cout << right << setw(30) << "合计:" << setw(10) << total << endl; return 0; }
17.5.2 未格式化 IO
// unformatted_io.cpp -- 未格式化IO #include <iostream> #include <fstream> #include <string> int main() { using namespace std; // ===== 单字节操作 ===== cout << "===== 单字节操作 =====" << endl; // get():读取单个字符(含空白) char ch; cout << "输入字符:"; cin.get(ch); cout << "读取到:'" << ch << "'" << endl; // peek():查看但不读取 char next = cin.peek(); cout << "下一个字符:'" << next << "'" << endl; // putback():放回字符 cin.putback(ch); // unget():撤销最后一次读取 // cin.unget(); // ===== 多字节操作 ===== cout << "\n===== 多字节操作 =====" << endl; // read():读取指定字节数 char buf[10]; cin.read(buf, 5); buf[5] = '\0'; cout << "read(5):" << buf << endl; // gcount():上次读取的字节数 cout << "gcount:" << cin.gcount() << endl; // ===== 文件定位 ===== cout << "\n===== 文件定位 =====" << endl; // 写入测试文件 { ofstream out("test.bin", ios::binary); out << "Hello, World!"; } // 读取并定位 ifstream in("test.bin", ios::binary); // tellg():获取当前位置 cout << "初始位置:" << in.tellg() << endl; // seekg():设置读取位置 in.seekg(7); // 从头移动7字节 cout << "移动到7后:" << in.tellg() << endl; char c; in.get(c); cout << "读取到:'" << c << "'" << endl; // 'W' // 从末尾定位 in.seekg(-6, ios::end); string rest; getline(in, rest); cout << "末尾前6字节:" << rest << endl; return 0; }
17.6 综合示例:日志分析系统
// log_analyzer.cpp -- 综合示例:日志分析系统 #include <iostream> #include <fstream> #include <sstream> #include <string> #include <regex> #include <map> #include <vector> #include <tuple> #include <algorithm> #include <iomanip> #include <random> // 日志条目 struct LogEntry { string timestamp; string level; string module; string message; }; class LogAnalyzer { private: vector<LogEntry> entries_; // 日志格式:[2024-01-15 10:30:45] [INFO] [Module] Message regex logPattern_{ R"(\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(\w+)\] \[(\w+)\] (.+))" }; public: // 解析日志文件 bool parseFile(const string& filename) { ifstream file(filename); if (!file) return false; string line; while (getline(file, line)) { smatch match; if (regex_match(line, match, logPattern_)) { entries_.push_back({ match[1], match[2], match[3], match[4] }); } } return true; } // 解析字符串 void parseString(const string& logs) { istringstream iss(logs); string line; while (getline(iss, line)) { smatch match; if (regex_match(line, match, logPattern_)) { entries_.push_back({ match[1], match[2], match[3], match[4] }); } } } // 按级别统计 map<string, int> countByLevel() const { map<string, int> counts; for (const auto& e : entries_) counts[e.level]++; return counts; } // 按模块统计 map<string, int> countByModule() const { map<string, int> counts; for (const auto& e : entries_) counts[e.module]++; return counts; } // 搜索包含关键词的日志 vector<LogEntry> search(const string& keyword) const { vector<LogEntry> results; regex kw(keyword, regex_constants::icase); for (const auto& e : entries_) if (regex_search(e.message, kw)) results.push_back(e); return results; } // 获取错误日志 vector<LogEntry> getErrors() const { vector<LogEntry> errors; for (const auto& e : entries_) if (e.level == "ERROR" || e.level == "CRITICAL") errors.push_back(e); return errors; } // 生成报告 void generateReport(ostream& os) const { os << "\n" << string(60, '=') << endl; os << "日志分析报告" << endl; os << string(60, '=') << endl; os << "\n总条目数:" << entries_.size() << endl; // 级别统计 os << "\n===== 按级别统计 =====" << endl; for (const auto& [level, count] : countByLevel()) os << setw(10) << level << ": " << count << " 条" << endl; // 模块统计 os << "\n===== 按模块统计 =====" << endl; for (const auto& [module, count] : countByModule()) os << setw(10) << module << ": " << count << " 条" << endl; // 错误日志 auto errors = getErrors(); if (!errors.empty()) { os << "\n===== 错误日志(" << errors.size() << "条)=====" << endl; for (const auto& e : errors) os << "[" << e.timestamp << "] [" << e.level << "] " << "[" << e.module << "] " << e.message << endl; } } }; // 生成测试日志 string generateTestLogs() { default_random_engine engine(42); uniform_int_distribution<int> levelDist(0, 3); uniform_int_distribution<int> moduleDist(0, 2); vector<string> levels = {"DEBUG", "INFO", "WARNING", "ERROR"}; vector<string> modules = {"Auth", "Database", "Network"}; vector<string> messages = { "用户登录成功", "数据库连接建立", "网络请求超时", "认证失败:密码错误", "查询执行完成", "连接池已满", "用户注销", "事务提交成功", "DNS解析失败" }; uniform_int_distribution<int> msgDist(0, messages.size() - 1); ostringstream oss; for (int i = 0; i < 20; i++) { int h = 10 + i / 4, m = (i * 15) % 60, s = i * 3 % 60; oss << "[2024-01-15 " << setfill('0') << setw(2) << h << ":" << setw(2) << m << ":" << setw(2) << s << "] " << "[" << levels[levelDist(engine)] << "] " << "[" << modules[moduleDist(engine)] << "] " << messages[msgDist(engine)] << "\n"; } return oss.str(); } int main() { using namespace std; LogAnalyzer analyzer; // 解析测试日志 string logs = generateTestLogs(); cout << "===== 原始日志 =====" << endl; cout << logs; analyzer.parseString(logs); // 生成报告 analyzer.generateReport(cout); // 搜索 cout << "\n===== 搜索\"失败\" =====" << endl; auto results = analyzer.search("失败"); for (const auto& e : results) cout << "[" << e.level << "] " << e.message << endl; return 0; }
📝 第17章知识点总结
| 知识点 | 核心要点 |
|---|
| tuple | make_tuple创建,get<N>访问,C++17结构化绑定解包 |
| tuple_size | tuple_size<T>::value获取成员数量 |
| tuple_element | tuple_element<N,T>::type获取第N个成员的类型 |
| tie | tie(a,b,c) = t解包tuple,ignore忽略某个成员 |
| tuple_cat | 拼接多个tuple |
| bitset | 固定大小的位集合,支持位运算,set/reset/flip/test |
| bitset 统计 | count()(1的个数)、any()、all()、none() |
| regex | regex_match(全匹配)、regex_search(搜索)、regex_replace(替换) |
| smatch | 存储匹配结果,[0]是完整匹配,[1]起是捕获组 |
| sregex_iterator | 遍历所有匹配结果 |
| 随机数引擎 | default_random_engine,用random_device或时间作种子 |
| 随机数分布 | uniform_int_distribution、uniform_real_distribution、normal_distribution |
| 格式化IO | setw/setprecision/fixed/scientific/left/right/setfill |
| 未格式化IO | get/peek/putback/read/gcount/seekg/tellg |