Python 3.x解析含转义特殊字符XML报invalid token如何解决
问题根因
该报错由Python 2与Python 3的字符串模型本质差异导致,和en-dash字符本身无关:
- Python 2的
str类型为原生字节串,代码中编写"\xe2\x80\x93"时,实际存储的是3个连续的UTF-8编码字节,对应en-dash(U+2013)字符。XML解析器读取内容时会按照头部声明的编码规则将字节流解码为正确字符,不会触发错误。 - Python 3的
str类型为Unicode字符串,同样编写"\xe2\x80\x93"时,会被解析为3个独立的Unicode码位:â(U+00E2)、控制字符U+0080、控制字符U+0093。XML 1.0规范明确禁止U+007F-U+009F范围内的控制字符出现在文档内容中,解析器检测到非法字符就会抛出not well-formed (invalid token)错误。
其他特殊字符触发同类报错的逻辑完全一致:原本应该作为多字节UTF-8序列被整体解码的内容,被Python 3错误拆分为单个Unicode码位,其中包含XML规范不允许的非法控制字符。
修复方案
1. 源头修复(推荐)
从XML生成逻辑入手彻底规避问题,适配Python 3的字符串规则:
- 所有写入XML的文本内容统一转换为Python 3的
str(Unicode)类型,禁止将UTF-8字节序列、字节转义字面量直接拼接到XML字符串中。如果拿到的是bytes类型内容,必须先调用.decode('utf-8')转成Unicode字符串再做后续处理。 - 不要手动拼接XML标签、属性和文本内容,使用标准库提供的XML构造API生成文档,比如
xml.dom.minidom的节点创建方法、xml.etree.ElementTree等。这类API会自动处理特殊字符转义、编码匹配问题,从根源避免非法字符、转义错误。 - 将XML头部的编码声明从非标准的
utf8修改为规范值UTF-8,兼容严格模式的XML解析器。
正确生成XML的示例代码:
import xml.dom.minidom from xml.dom.minidom import Document # 初始化文档 doc = Document() root = doc.createElement("properties") doc.appendChild(root) # 构造节点 entry_node = doc.createElement("entry") entry_node.setAttribute("key", "name") # 直接传入Unicode字符串,包含en-dash等特殊字符无需额外转义 text_node = doc.createTextNode("AB&R - RFA #3 – Alignment") entry_node.appendChild(text_node) root.appendChild(entry_node) # 输出格式化后的XML,编码自动匹配声明 pretty_xml = doc.toprettyxml(indent=" ", newl="\n", encoding="UTF-8")
2. 兜底兼容方案
如果暂时无法修改上游XML生成逻辑,可以在解析前对非法字符串做清洗转换,适配现有错误格式的内容:
import xml.dom.minidom def repair_misencoded_xml(xml_str: str) -> str: # 还原被错误拆分为单个Unicode码位的UTF-8字节序列 # 原理:错误生成的U+00XX字符和latin-1编码的0xXX字节一一对应 repaired_bytes = xml_str.encode("latin-1", errors="ignore") return repaired_bytes.decode("utf-8", errors="replace") # 解析前先修复编码 fixed_content = repair_misencoded_xml(xml_string) xml_parsed = xml.dom.minidom.parseString(fixed_content) return xml_parsed.toprettyxml(" ", "\n")
该方案可以覆盖绝大多数非ASCII特殊字符导致的同类解析错误,但仅作为临时兼容手段,长期维护仍建议从生成端修正逻辑。
内容的提问来源于stack exchange,提问作者George Hernando
相关产品推荐
相关产品推荐

