#include "xml_opr.h" #include #include #include #include #include "../public_def.h" namespace fs = std::filesystem; CXmlOpr::CXmlOpr() = default; CXmlOpr::~CXmlOpr() = default; bool CXmlOpr::open(const std::string& xml_path) { #ifdef _WIN32 if (doc_.LoadFile(CUtil::utf8_to_gbk(xml_path).c_str()) != tinyxml2::XML_SUCCESS) { return false; } xml_path_ = CUtil::utf8_to_gbk(xml_path); #else if (doc_.LoadFile(xml_path.c_str()) != tinyxml2::XML_SUCCESS) { return false; } xml_path_ = xml_path; #endif return true; } bool CXmlOpr::backup_file(const std::string& desti_folder, const std::string& time) { if (!fs::exists(xml_path_)) { return false; } if (!fs::exists(desti_folder)) { fs::create_directories(desti_folder); } fs::path file_path = fs::path(xml_path_); fs::path des = fs::path(desti_folder) .append(file_path.stem().string() + "_" + time + ".xml"); return fs::copy_file(xml_path_, des); } void CXmlOpr::set_baseinfo(const OneGroupIni& base) { opr_base_ = base; } bool CXmlOpr::get_all_elements(std::vector& vec, const std::string& unit) { vec.clear(); parent_node2_ = parent_node_; if (!unit.empty()) { parent_node2_ = parent_node2_->FirstChildElement(unit.c_str()); } if (!opr_base_.relative_nodes.empty()) { auto v = CUtil::splitString(opr_base_.relative_nodes, ","); // 向下子项跳跃 for (const auto& it : v) { parent_node2_ = parent_node2_->FirstChildElement(it.c_str()); } } auto purpose_node = parent_node2_->FirstChildElement(opr_base_.item_key.c_str()); while (purpose_node) { vec.push_back(purpose_node); purpose_node = purpose_node->NextSiblingElement(); } return true; } bool CXmlOpr::parse_xml(std::vector& vec) { std::string next_node{}; std::string node_path = opr_base_.item_key; keys_.clear(); auto keys = CUtil::splitString(opr_base_.propertis, ","); for (const auto& item : keys) { if (item.empty()) { continue; } keys_.push_back(item); } auto nodes = CUtil::splitString(opr_base_.main_nodes, "/"); for (const auto& item : nodes) { if (item.empty()) { continue; } if (parent_node_ == nullptr) { parent_node_ = doc_.FirstChildElement(item.c_str()); } else { parent_node_ = parent_node_->FirstChildElement(item.c_str()); } } if (parent_node_ == nullptr) { return false; } if (opr_base_.is_same) { units_.clear(); auto p = parent_node_->FirstChildElement(); while (p) { units_.push_back(p->Name()); p = p->NextSiblingElement(); } if (!units_.empty()) { get_all_elements(vec, units_[0]); } } else { get_all_elements(vec); } return true; } bool CXmlOpr::get_all_unit(std::vector& units) { units = units_; return true; } // 排序后(仅指针排序)的节点进行复制(让实际内容有序),删除原始节点 void CXmlOpr::copy_and_del(std::vector& vec, std::vector& out) { out.clear(); // 先找到最后一个节点 Element_t* last_node = parent_node2_->LastChildElement(opr_base_.item_key.c_str()); Element_t* last_node_bk = last_node; if (last_node == nullptr) { return; } for (const auto& data : vec) { Element_t* n = copy_element(data); out.push_back(n); insert_brother_node(last_node, n); last_node = n; } // 删除原有的节点 Element_t* fnode = parent_node2_->FirstChildElement(opr_base_.item_key.c_str()); Element_t* fnext = fnode->NextSiblingElement(); while (fnode != last_node_bk) { parent_node2_->DeleteChild(fnode); fnode = fnext; fnext = fnode->NextSiblingElement(); } parent_node2_->DeleteChild(last_node_bk); } void CXmlOpr::insert_brother_node(Element_t* brother, Element_t* newer) { if (!brother || !newer) { return; } parent_node2_->InsertAfterChild(brother, newer); } Element_t* CXmlOpr::copy_element(Element_t* ele) { if (!ele) { return nullptr; } Element_t* ret = doc_.NewElement(ele->Name()); const auto* attribute = ele->FirstAttribute(); while (attribute) { ret->SetAttribute(attribute->Name(), attribute->Value()); attribute = attribute->Next(); } return ret; } bool CXmlOpr::check_valid_xml_data(const std::string& data) { tinyxml2::XMLDocument doc; doc.Parse(data.c_str()); if (doc.Error()) { return false; } return true; } bool CXmlOpr::check_same_struct(const std::string& data) { auto* own_ele = parent_node2_->FirstChildElement(opr_base_.item_key.c_str()); if (own_ele == nullptr) { return true; } tinyxml2::XMLDocument doc; doc.Parse(data.c_str()); if (doc.Error()) { return false; } const auto* import_ele = doc.FirstChildElement(opr_base_.item_key.c_str()); if (import_ele == nullptr) { return false; } const auto* attribute = own_ele->FirstAttribute(); int own_cnt = 0; while (attribute) { ++own_cnt; if (import_ele->FindAttribute(attribute->Name()) == nullptr) { return false; } attribute = attribute->Next(); } const auto* attr_import = import_ele->FirstAttribute(); int import_cnt = 0; while (attr_import) { ++import_cnt; attr_import = attr_import->Next(); } if (import_cnt != own_cnt) { return false; } return true; } // Warning: 不检查 xml 格式合法性,请自行调用 check_valid_xml_data // 且导入前每条数据请自行使用 check_same_struct 检测。 bool CXmlOpr::import_newer_data(const std::vector& vec, std::size_t& success_count) { success_count = 0; auto* last_item = parent_node2_->LastChildElement(opr_base_.item_key.c_str()); if (last_item == nullptr) { return false; } // 检查重复性 for (const auto& data : vec) { tinyxml2::XMLDocument doc; doc.Parse(data.c_str()); auto* item = doc.FirstChildElement(opr_base_.item_key.c_str()); if (item == nullptr) { continue; } const char* key_str = item->Attribute(keys_[0].c_str()); if (check_key_exists(std::string(key_str))) { continue; } ++success_count; auto* nitem = copy_element(item); insert_brother_node(last_item, nitem); last_item = nitem; } return true; } std::string CXmlOpr::handle_space(const std::string& content) { std::string result; size_t pos = 0; size_t len = content.length(); // Define a regular expression for the operators std::regex operators_regex(R"(&&|!=|==|<|>|\|\||\+|-|\*|/)"); while (pos < len) { size_t start_quote = content.find('"', pos); if (start_quote == std::string::npos) { result.append(content.substr(pos)); break; } // Append everything before the first quote result.append(content.substr(pos, start_quote - pos)); // Find the ending quote size_t end_quote = content.find('"', start_quote + 1); if (end_quote == std::string::npos) { result.append(content.substr(start_quote)); break; } // Extract the quoted content std::string quoted_content = content.substr(start_quote + 1, end_quote - start_quote - 1); std::string processed_content = std::regex_replace(quoted_content, operators_regex, " $& "); // Process quoted content to replace multiple spaces with a single space std::istringstream iss(processed_content); std::ostringstream oss; std::string word; bool first = true; while (iss >> word) { if (!first) { oss << ' '; } oss << word; first = false; } // Append processed content with quotes result.append("\"" + oss.str() + "\""); // Move past the ending quote pos = end_quote + 1; } return result; } bool CXmlOpr::handle_transfer(const std::string& path) { std::ifstream file(path); if (!file.is_open()) { return false; } std::istreambuf_iterator ifter(file); std::istreambuf_iterator iter; std::string content(ifter, iter); file.close(); size_t pos = 0; std::string result = content; // Replace XML entities with their corresponding characters while ((pos = result.find("&", pos)) != std::string::npos) { result.replace(pos, 5, "&"); pos += 1; // Move past the replaced character } pos = 0; while ((pos = result.find("<", pos)) != std::string::npos) { result.replace(pos, 4, "<"); pos += 1; // Move past the replaced character } pos = 0; while ((pos = result.find(">", pos)) != std::string::npos) { result.replace(pos, 4, ">"); pos += 1; // Move past the replaced character } pos = 0; while ((pos = result.find(""", pos)) != std::string::npos) { result.replace(pos, 6, "\""); pos += 1; // Move past the replaced character } pos = 0; while ((pos = result.find("'", pos)) != std::string::npos) { result.replace(pos, 6, "'"); pos += 1; // Move past the replaced character } // 处理空格格式化 std::string sec_handle = handle_space(result); std::ofstream ofile(path); if (!ofile.is_open()) { return false; } ofile << sec_handle; return true; } void CXmlOpr::del_element(Element_t* ele) { parent_node2_->DeleteChild(ele); } bool CXmlOpr::check_key_exists(const Property_t& property) { if (keys_.size() < 1 || property.size() < 1) { return false; } return check_key_exists(property[0].value); } bool CXmlOpr::check_key_exists(const std::string& key) { if (keys_.size() < 1 || key.empty()) { return false; } Element_t* purpose_node = parent_node2_->FirstChildElement(opr_base_.item_key.c_str()); while (purpose_node) { const char* value = purpose_node->Attribute(keys_[0].c_str()); if (key == std::string(value)) { return true; } purpose_node = purpose_node->NextSiblingElement(); } return false; } bool CXmlOpr::save() { auto ret = doc_.SaveFile(xml_path_.c_str()); if (ret != tinyxml2::XML_SUCCESS) { return false; } return true; } bool CXmlOpr::handle_save(const std::string& path) { if (!open(path)) { return false; } auto ret = doc_.SaveFile(xml_path_.c_str()); if (ret != tinyxml2::XML_SUCCESS) { return false; } if (!handle_transfer(xml_path_)) { return false; } return true; } void CXmlOpr::get_attributes(Element_t* ele, Property_t& vec) { if (ele == nullptr) { return; } vec.clear(); const auto* attribute = ele->FirstAttribute(); while (attribute) { vec.emplace_back(attribute->Name(), attribute->Value()); attribute = attribute->Next(); } } void CXmlOpr::attributes_to_element(Element_t* ele, const Property_t& vec) { if (ele == nullptr) { return; } for (const auto& data : vec) { ele->SetAttribute(data.key.c_str(), data.value.c_str()); } } SKeyValue::SKeyValue(const char* pkey, const char* pvalue) { key = std::string(pkey); value = std::string(pvalue); }