如何修改C++代码解析类Pango标记,支持同层级多标签生成树形结构?
问题描述
我需要将Pango风格标记解析为树形结构,叶子节点为文本元素,分支节点为带属性的标签元素,示例输入如下:
<b>This is bold</b>, <i>italic and <span color="red">red text</span></i> !
现有C++代码(不含类定义)在去掉<b></b>部分时可正常运行,但仅能处理每个层级单个标签,同层级存在多个标签时解析失败,代码如下:
bool parseStartTag(const std::string& s, size_t start, size_t end, std::string& tag_name, std::map<std::string, std::string>& attrs) { size_t tag_name_end = s.find_first_of(" \t\n", start); if (tag_name_end == std::string::npos or tag_name_end >= end) { tag_name = s.substr(start, end - start); return true; } else { tag_name = s.substr(start, tag_name_end - start); } start = tag_name_end; char quote = '\0'; std::string attr_name = ""; std::string attr_value = ""; while (start <= end) { if (quote != '\0') { if (s[start] == quote) { quote = '\0'; attrs[attr_name] = attr_value; attr_name = ""; attr_value = ""; } else { attr_value += s[start]; } } else if ((s[start] == ' ' or s[start] == '\t' or s[start] == '\n') and quote == '\0') { } else if (s[start] == '"' or s[start] == '\'') { quote = s[start]; } else if (s[start] != '=') { attr_name += s[start]; } start++; } return true; } std::shared_ptr<SGMarkupTag> parse(const std::string& s, std::string tag_name) { std::shared_ptr<SGMarkupTag> tag = std::make_shared<SGMarkupTag>(); tag->setName(tag_name); size_t opening_index = 0; size_t closing_index = 0; size_t tag_content_start = 0; size_t tag_content_end = 0; while (closing_index != std::string::npos) { opening_index = s.find("<", closing_index); std::string text_string; if (opening_index != std::string::npos) { text_string = s.substr(closing_index, opening_index - closing_index); } else { text_string = s.substr(closing_index); } tag->addText(text_string); if (opening_index == std::string::npos) { return tag; } closing_index = s.find(">", opening_index); if (opening_index != std::string::npos and closing_index == std::string::npos) { SG_LOG(SG_GENERAL, SG_ALERT, "Markup parse error: Missing closing bracket for tag after opening bracket at char " << opening_index << ":"); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << s); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << std::string(opening_index, '-') << "^"); return tag; } std::string tag_name = ""; std::map<std::string, std::string> attrs; if (!parseStartTag(s, opening_index + 1, closing_index, tag_name, attrs)) { return tag; }; tag_content_start = closing_index + 1; opening_index = closing_index + s.substr(closing_index).rfind("</"); if (opening_index == std::string::npos) { SG_LOG(SG_GENERAL, SG_ALERT, "Markup parse error: Missing closing tag after opening tag at char " << closing_index - 1 << ":"); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << s); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << std::string(closing_index - 1, '-') << "^"); return tag; } tag_content_end = opening_index; closing_index = opening_index + s.substr(opening_index).rfind(">"); if (closing_index == std::string::npos) { SG_LOG(SG_GENERAL, SG_ALERT, "Markup parse error: Missing closing bracket for tag after opening bracket at char " << opening_index << ":"); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << s); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << std::string(opening_index, '-') << "^"); return tag; } std::string closing_tag_name = s.substr(opening_index + 2, closing_index - opening_index - 2); if (closing_tag_name != tag_name) { SG_LOG(SG_GENERAL, SG_ALERT, "Markup parse error: Unmatched closing tag '" << closing_tag_name << "' at char " << opening_index + 1 << " for opening tag '" << tag_name << "':"); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << s); SG_LOG(SG_GENERAL, SG_ALERT, "\t" << std::string(opening_index + 1, '-') << "^"); return tag; } std::shared_ptr<SGMarkupElement> sub_tag = parse(s.substr(tag_content_start, tag_content_end - tag_content_start), tag_name); sub_tag->setAttributes(attrs); sub_tag->setParent(tag); sub_tag->setRoot(tag->getRoot()); tag->addChild(sub_tag); closing_index++; } tag->addText(s.substr(closing_index)); return tag; }
请问应如何修改parse函数中的while循环,使其支持同层级多个标签?希望得到修改方向的提示。
修改方向提示
用标签栈替代
rfind定位闭合标签
当前代码用rfind("</")直接跳到文本段最后一个闭合标签,会忽略中间的同级或嵌套标签。需要维护一个标签栈:遇到起始标签时压栈,遇到闭合标签时弹栈匹配,以此准确定位当前标签的闭合位置,避免跳过同级标签。重构循环的逐段处理逻辑
当前循环处理完一个标签后直接跳到其闭合标签之后,但未考虑后续同级标签。调整流程为:每次循环先处理当前位置到下一个<的文本;再判断<后是起始标签还是闭合标签——如果是起始标签,递归解析其内容直到匹配闭合标签,将子标签加入当前节点后移动处理位置;如果是闭合标签,终止当前层级解析返回上层。改用全局索引位置替代子串截取
当前通过子串截取处理标签内容,容易造成索引混乱。可以用引用传递的size_t& current_pos作为全局处理指针,所有层级的解析共享这个指针,避免子串带来的索引偏移问题。提前区分起始标签和闭合标签
现在默认所有<都是起始标签,需要在处理<时先检查后续字符是否为/:如果是,说明是闭合标签,直接结束当前层级解析;如果不是,再按起始标签处理。
内容的提问来源于stack exchange,提问作者TheEagle
相关产品推荐
相关产品推荐

