C++20实现Python多层字典结构及标准库二进制序列化方案
解决方案:Python多层字典转C++结构+标准库二进制序列化
一、C++对应数据结构设计
针对你描述的Python多层字典结构,C++中可以用**std::variant(变体类型)**结合std::map来实现类型匹配,同时保证键的稳定性:
#include <map> #include <vector> #include <variant> #include <string> // 底层值类型:整数、整数列表、int-int字典的变体 using TicTacToeValue = std::variant<int, std::vector<int>, std::map<int, int>>; // 第二层字典:字符串键 -> 变体值 using TicTacToeSubDict = std::map<std::string, TicTacToeValue>; // 第一层字典:字符串键 -> 第二层字典 using TicTacToeStateDict = std::map<std::string, TicTacToeSubDict>;
- 用
std::map而非std::unordered_map是为了保证键的有序性,序列化后结构更稳定;若追求性能,也可替换为unordered_map(C++20中std::string的哈希实现已标准化,无需额外处理)。 std::variant是C++17引入的类型,完美适配Python中同一键对应不同类型值的场景。
二、二进制序列化与反序列化(纯标准库实现)
C++标准库没有现成的二进制序列化工具,但可以基于std::ostream/std::istream手动实现,通过类型标签确保反序列化时的类型稳定性。
1. 序列化逻辑
#include <fstream> #include <cstdint> #include <stdexcept> // 序列化整数 void serialize(std::ostream& os, int value) { const std::uint8_t type_tag = 0; // 标记当前值为int os.write(reinterpret_cast<const char*>(&type_tag), sizeof(type_tag)); os.write(reinterpret_cast<const char*>(&value), sizeof(value)); } // 序列化整数列表 void serialize(std::ostream& os, const std::vector<int>& vec) { const std::uint8_t type_tag = 1; // 标记当前值为vector<int> os.write(reinterpret_cast<const char*>(&type_tag), sizeof(type_tag)); const std::size_t size = vec.size(); os.write(reinterpret_cast<const char*>(&size), sizeof(size)); for (int num : vec) { os.write(reinterpret_cast<const char*>(&num), sizeof(num)); } } // 序列化int-int字典 void serialize(std::ostream& os, const std::map<int, int>& map_val) { const std::uint8_t type_tag = 2; // 标记当前值为map<int,int> os.write(reinterpret_cast<const char*>(&type_tag), sizeof(type_tag)); const std::size_t size = map_val.size(); os.write(reinterpret_cast<const char*>(&size), sizeof(size)); for (const auto& pair : map_val) { os.write(reinterpret_cast<const char*>(&pair.first), sizeof(pair.first)); os.write(reinterpret_cast<const char*>(&pair.second), sizeof(pair.second)); } } // 序列化变体类型 void serialize(std::ostream& os, const TicTacToeValue& var) { std::visit([&os](const auto& val) { serialize(os, val); }, var); } // 序列化第二层字典 void serialize(std::ostream& os, const TicTacToeSubDict& sub_dict) { const std::size_t size = sub_dict.size(); os.write(reinterpret_cast<const char*>(&size), sizeof(size)); for (const auto& pair : sub_dict) { // 先序列化字符串键的长度,再序列化内容 const std::size_t str_len = pair.first.size(); os.write(reinterpret_cast<const char*>(&str_len), sizeof(str_len)); os.write(pair.first.data(), str_len); // 序列化对应的值 serialize(os, pair.second); } } // 序列化顶层字典 void serialize(std::ostream& os, const TicTacToeStateDict& state_dict) { const std::size_t size = state_dict.size(); os.write(reinterpret_cast<const char*>(&size), sizeof(size)); for (const auto& pair : state_dict) { const std::size_t str_len = pair.first.size(); os.write(reinterpret_cast<const char*>(&str_len), sizeof(str_len)); os.write(pair.first.data(), str_len); serialize(os, pair.second); } } // 保存到二进制文件的便捷函数 void save_tic_tac_toe_states(const std::string& file_path, const TicTacToeStateDict& states) { std::ofstream file(file_path, std::ios::binary); if (!file) { throw std::runtime_error("Failed to open file for writing"); } serialize(file, states); }
2. 反序列化逻辑
// 反序列化整数 int deserialize_int(std::istream& is) { int value; is.read(reinterpret_cast<char*>(&value), sizeof(value)); return value; } // 反序列化整数列表 std::vector<int> deserialize_vector_int(std::istream& is) { std::size_t size; is.read(reinterpret_cast<char*>(&size), sizeof(size)); std::vector<int> vec(size); for (std::size_t i = 0; i < size; ++i) { is.read(reinterpret_cast<char*>(&vec[i]), sizeof(vec[i])); } return vec; } // 反序列化int-int字典 std::map<int, int> deserialize_map_int_int(std::istream& is) { std::size_t size; is.read(reinterpret_cast<char*>(&size), sizeof(size)); std::map<int, int> map_val; for (std::size_t i = 0; i < size; ++i) { int key, val; is.read(reinterpret_cast<char*>(&key), sizeof(key)); is.read(reinterpret_cast<char*>(&val), sizeof(val)); map_val[key] = val; } return map_val; } // 反序列化变体类型 TicTacToeValue deserialize_value(std::istream& is) { std::uint8_t type_tag; is.read(reinterpret_cast<char*>(&type_tag), sizeof(type_tag)); switch (type_tag) { case 0: return deserialize_int(is); case 1: return deserialize_vector_int(is); case 2: return deserialize_map_int_int(is); default: throw std::runtime_error("Unknown type tag in variant"); } } // 反序列化第二层字典 TicTacToeSubDict deserialize_sub_dict(std::istream& is) { std::size_t size; is.read(reinterpret_cast<char*>(&size), sizeof(size)); TicTacToeSubDict sub_dict; for (std::size_t i = 0; i < size; ++i) { std::size_t str_len; is.read(reinterpret_cast<char*>(&str_len), sizeof(str_len)); std::string key(str_len, '\0'); is.read(&key[0], str_len); TicTacToeValue val = deserialize_value(is); sub_dict[key] = val; } return sub_dict; } // 反序列化顶层字典 TicTacToeStateDict deserialize_tic_tac_toe_states(std::istream& is) { std::size_t size; is.read(reinterpret_cast<char*>(&size), sizeof(size)); TicTacToeStateDict state_dict; for (std::size_t i = 0; i < size; ++i) { std::size_t str_len; is.read(reinterpret_cast<char*>(&str_len), sizeof(str_len)); std::string key(str_len, '\0'); is.read(&key[0], str_len); TicTacToeSubDict sub_dict = deserialize_sub_dict(is); state_dict[key] = sub_dict; } return state_dict; } // 从二进制文件加载的便捷函数 TicTacToeStateDict load_tic_tac_toe_states(const std::string& file_path) { std::ifstream file(file_path, std::ios::binary); if (!file) { throw std::runtime_error("Failed to open file for reading"); } return deserialize_tic_tac_toe_states(file); }
三、使用示例
#include <iostream> int main() { // 构建测试数据 TicTacToeStateDict states; TicTacToeSubDict sub_dict1; sub_dict1["win_count"] = 5; sub_dict1["move_sequence"] = std::vector<int>{1,3,5}; sub_dict1["score_map"] = std::map<int, int>{{0, 10}, {1, -5}}; states["state_xxx"] = sub_dict1; // 保存与加载测试 try { save_tic_tac_toe_states("tictactoe_states.bin", states); std::cout << "States saved successfully\n"; TicTacToeStateDict loaded_states = load_tic_tac_toe_states("tictactoe_states.bin"); std::cout << "Loaded " << loaded_states.size() << " states\n"; // 访问加载的数据 auto& loaded_sub = loaded_states["state_xxx"]; if (std::holds_alternative<int>(loaded_sub["win_count"])) { std::cout << "Win count: " << std::get<int>(loaded_sub["win_count"]) << "\n"; } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << "\n"; } return 0; }
四、关键注意事项
- 类型稳定性:
type_tag的数值和顺序绝对不能随意修改,否则反序列化会直接失败。 - 跨平台兼容:如果需要在不同操作系统间传输文件,要处理字节序问题(可通过
htonl/ntohl转换整数的字节序)。 - VS2022配置:确保项目启用C20标准(右键项目→属性→C/C→语言→选择ISO C20标准(/std:c20))。
- 性能适配:8533个状态的数据量很小,这套实现完全够用;若后续数据量增大,可优化为批量写入/读取逻辑。
内容的提问来源于stack exchange,提问作者Ξένη Γήινος
相关产品推荐
相关产品推荐

