C++ Primer 第17章:标准库特殊设施
·
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 |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)