You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:17:02