优化了xml修改和表格逆生成功能

This commit is contained in:
lnk
2026-07-14 15:14:54 +08:00
parent 70c1534d96
commit 6bfaa32db4
74 changed files with 15935 additions and 1804129 deletions

View File

@@ -14,6 +14,26 @@
#include <unordered_map>
#include <vector>
/*
* icd_mapper.cpp
*
* 功能:把外部 ICD/XML 中 LN/DOI 的 desc 描述补写到工作簿
* “未匹配的原始数据”工作表的“icd中代表的字段”列。
*
* 输入:
* icdPath 外部 ICD/XML 文件,可能是 UTF-8也可能声明/实际使用 GBK/GB2312。
* workbookPath main.cpp 生成的 Excel 2003 XML 格式 .xls。
*
* 输出:
* 直接修改 workbookPath。message 返回解析结果、匹配数量或错误原因。
*
* 核心匹配规则:
* 1. 从 ICD 的每个 LN 节点读取 lnClass + inst并遍历其 DOI 子节点;
* 2. 组成 DO keylnClass + inst + "$MX$" + DOI/@name
* 3. 工作簿未匹配页的“原始路径”如果等于该 key或以 key + "$" 开头,
* 就认为该原始 MMS 路径属于这个 DOI把 DOI/@desc 写入目标列。
*/
namespace fs = std::filesystem;
namespace {
@@ -46,6 +66,7 @@ bool declaresGbEncoding(const std::string& text) {
}
bool isLikelyUtf8(const std::string& text) {
// 只做 UTF-8 字节合法性判断,不判断语义;合法就优先按 UTF-8 给 tinyxml2。
int remaining = 0;
for (unsigned char c : text) {
if (remaining == 0) {
@@ -63,6 +84,7 @@ bool isLikelyUtf8(const std::string& text) {
}
std::string ansiToUtf8(const std::string& text) {
// 936 是 Windows 简体中文代码页,兼容常见 GBK/GB2312 ICD 文件。
if (text.empty()) return "";
int wideLen = MultiByteToWideChar(936, 0, text.data(), (int)text.size(), nullptr, 0);
if (wideLen <= 0) return text;
@@ -76,6 +98,7 @@ std::string ansiToUtf8(const std::string& text) {
std::string loadXmlText(const fs::path& path) {
std::string raw = readAllBinary(path);
// ICD 声明 GB 编码或字节本身不像 UTF-8 时,先转成 UTF-8 再交给 tinyxml2。
if (declaresGbEncoding(raw) || !isLikelyUtf8(raw)) return ansiToUtf8(raw);
return raw;
}
@@ -106,6 +129,7 @@ void collectIcdDescriptions(const tinyxml2::XMLElement* element,
std::string doiName = attr(child, "name");
std::string desc = attr(child, "desc");
if (doiName.empty() || desc.empty()) continue;
// 这里构造的是 origin 原始路径前缀。后面路径可继续带 $mag$f 等 DA 后缀。
std::string key = lnClass + inst + "$MX$" + doiName;
descByDo[key] = desc;
}
@@ -155,6 +179,7 @@ tinyxml2::XMLElement* cellAt(tinyxml2::XMLElement* row, int col) {
tinyxml2::XMLElement* ensureCell(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* row, int col) {
if (tinyxml2::XMLElement* existing = cellAt(row, col)) return existing;
// Excel XML 允许通过 ss:Index 跳列;补列时直接设置 ss:Index避免改动前面已有单元格。
tinyxml2::XMLElement* cell = doc.NewElement("Cell");
cell->SetAttribute("ss:Index", col);
row->InsertEndChild(cell);
@@ -175,6 +200,7 @@ std::vector<std::string> sortedKeysByLength(const std::unordered_map<std::string
std::vector<std::string> keys;
keys.reserve(values.size());
for (const auto& kv : values) keys.push_back(kv.first);
// 长路径优先,避免短 key 先命中长 key 的前缀。
std::sort(keys.begin(), keys.end(), [](const std::string& a, const std::string& b) {
if (a.size() != b.size()) return a.size() > b.size();
return a < b;
@@ -183,6 +209,7 @@ std::vector<std::string> sortedKeysByLength(const std::unordered_map<std::string
}
bool pathMatchesDo(const std::string& rawPath, const std::string& doKey) {
// rawPath 可以正好是 DO也可以是 DO$DA...;必须在 "$" 边界匹配,避免误命中相似前缀。
return rawPath == doKey ||
(rawPath.size() > doKey.size() &&
rawPath.compare(0, doKey.size(), doKey) == 0 &&
@@ -230,6 +257,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
const std::string& workbookPath,
std::string* message) {
try {
// 第一步:解析 ICD得到 “DO 路径前缀 -> 中文描述”。
auto descByDo = parseIcdDescriptions(fs::u8path(icdPath));
if (descByDo.empty()) {
if (message) *message = "ICD 中没有解析到 LN/DOI desc";
@@ -237,6 +265,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
}
std::vector<std::string> orderedKeys = sortedKeysByLength(descByDo);
// 第二步:读取 main.cpp 输出的工作簿。该 .xls 实际是 XML可直接用 tinyxml2 修改。
tinyxml2::XMLDocument doc;
std::string workbookXml = readAllBinary(fs::u8path(workbookPath));
tinyxml2::XMLError rc = doc.Parse(workbookXml.c_str(), workbookXml.size());
@@ -245,6 +274,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
return false;
}
// 第三步:只处理“未匹配的原始数据”页,因为已匹配页已经有明确映射关系。
tinyxml2::XMLElement* sheet = findWorksheet(doc, "未匹配的原始数据");
if (!sheet) {
if (message) *message = "没有找到 sheet: 未匹配的原始数据";
@@ -259,6 +289,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
return false;
}
// 第四步:逐行读取原始路径,按 DO 前缀回填 DOI 描述。
int matched = 0;
for (tinyxml2::XMLElement* row = header ? header->NextSiblingElement("Row") : nullptr;
row;