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

复刻含类XML控制码的游戏对话系统的解析问题求助

我之前在复刻游戏对话系统时也碰到过一模一样的问题——想用类XML的控制码但又没法套根节点,下面这几个方案亲测好用,你可以根据自己的需求选:

方案1:正则表达式解析(适合简单标签场景)

如果你的控制码没有嵌套需求(比如不会出现<co red><b>text</b></co>这种情况),正则是最快的实现方式。核心思路是匹配所有的控制码标签和中间的普通文本,然后逐个处理。

比如针对你的<co>标签,可以用这样的正则模式:

import re

pattern = re.compile(r'(<co ([^>]+)>(.*?)</co>|[^<]+)')
text = "Hello <co FF0000FF>World!</co> This is <co 00FF00FF>green text</co> or just plain text."

matches = pattern.findall(text)
for match in matches:
    if match[0].startswith('<co'):
        # 处理颜色标签:match[1]是颜色值,match[2]是文本内容
        color = match[1]
        content = match[2]
        print(f"渲染文本: {content},颜色: {color}")
    else:
        # 处理普通文本
        print(f"渲染普通文本: {match[0]}")

优点:代码量极少,快速上手,适合标签逻辑简单的对话系统。
缺点:无法处理嵌套标签,标签如果有复杂属性(比如带空格的)需要调整正则,容错性稍差。

方案2:自定义状态机解析(支持嵌套标签)

如果需要支持标签嵌套,或者以后要扩展更多复杂控制码(比如<shake>抖动文本、<delay>延迟显示),状态机是更可靠的选择。核心是逐字符扫描文本,维护当前的解析状态(普通文本/标签开始/标签结束)。

举个简单的Python实现示例:

def parse_dialogue(text):
    tokens = []
    current_text = []
    i = 0
    n = len(text)
    
    while i < n:
        if text[i] == '<':
            # 保存当前积累的普通文本
            if current_text:
                tokens.append(('text', ''.join(current_text)))
                current_text = []
            i += 1
            # 解析标签名和属性
            tag_parts = []
            while i < n and text[i] != '>':
                tag_parts.append(text[i])
                i += 1
            tag_str = ''.join(tag_parts)
            if tag_str.startswith('/'):
                # 结束标签
                tokens.append(('end_tag', tag_str[1:]))
            else:
                # 开始标签,拆分标签名和属性(比如co FF0000FF)
                tag_name, *attrs = tag_str.split()
                tokens.append(('start_tag', tag_name, attrs))
            i += 1  # 跳过'>'
        else:
            # 积累普通文本
            current_text.append(text[i])
            i += 1
    # 处理最后一段普通文本
    if current_text:
        tokens.append(('text', ''.join(current_text)))
    return tokens

# 测试
text = "Hello <co FF0000FF>World <b>bold</b></co>!"
tokens = parse_dialogue(text)
for token in tokens:
    print(token)

这个解析器会把文本转换成('text', 内容)、('start_tag', 标签名, 属性列表)、('end_tag', 标签名)这样的 token,之后你可以根据这些token来处理文本渲染逻辑。

优点:完全自定义,支持任意嵌套和复杂标签,扩展性极强。
缺点:需要自己实现扫描逻辑,代码量比正则多,但逻辑清晰,维护起来不难。

方案3:临时包装根节点用XML解析器处理

如果你还是想复用成熟的XML解析库,可以在解析前给文本套一个临时的根标签,比如<root>...</root>,然后用标准XML解析器解析,之后再遍历节点处理。

比如用Python的xml.etree.ElementTree:

import xml.etree.ElementTree as ET

def parse_with_xml(text):
    # 临时包装根节点
    wrapped_text = f"<root>{text}</root>"
    # 解析XML
    root = ET.fromstring(wrapped_text)
    
    # 遍历根节点的子节点和文本节点
    def traverse(node):
        if node.text:
            yield ('text', node.text)
        for child in node:
            yield ('start_tag', child.tag, child.attrib)
            yield from traverse(child)
            yield ('end_tag', child.tag)
        if node.tail:
            yield ('text', node.tail)
    
    return list(traverse(root))

# 测试
text = "Hello <co color='FF0000FF'>World!</co> Plain text."
tokens = parse_with_xml(text)
for token in tokens:
    print(token)

注意这里要把你的控制码属性改成XML格式(比如<co color='FF0000FF'>而不是<co FF0000FF>),如果不想改的话,可以先预处理文本把属性转换成XML属性格式,比如把<co ([^>]+)>替换成<co value='$1'>。

优点:利用成熟的XML解析库,自动处理嵌套、属性解析,容错性好。
缺点:需要适配XML的语法规则(比如属性要加引号),如果原控制码有不符合XML的地方(比如未转义的&)需要提前处理。

最后选哪个方案?如果只是简单的颜色、字体大小这类无嵌套的标签,正则足够;如果要做复杂的对话效果(比如嵌套的样式、动画标签),状态机更灵活;如果想偷懒用现成库,就选临时包装根节点的方法。

内容的提问来源于stack exchange,提问作者user4148144

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:37:53