XML解析报错“not well-formed”十有八九是UTF-8 BOM(\ufeff)导致,因XML解析器将其视为非法字符;需在读取时手动跳过BOM:Python用二进制读取后切片,Node.js用Buffer截断,Java用BOMInputStream或自定义过滤。

Python 读取 XML 文件报 XMLSyntaxError: not well-formed (invalid token)
十有八九是文件开头藏了 UTF-8 BOM(\ufeff),XML 解析器把它当成了非法字符。Python 默认用 open() 读文本时会自动解码,但不会自动剥离 BOM;而 xml.etree.ElementTree.parse() 这类底层解析器对开头的 BOM 非常敏感。
实操建议:
- 优先用二进制模式读取,手动跳过 BOM:先读前 3 字节判断是否为
b'\xef\xbb\xbf',再解码剩余内容 - 别依赖
encoding='utf-8-sig'—— 它虽能自动去 BOM,但只适用于open()直接返回字符串的场景;若后续要传给ET.fromstring(),必须确保输入是纯字符串,不能混入 BOM - 如果用
requests.get().content拿到响应体,它本身就是 bytes,BOM 会原样保留,需显式处理
with open('data.xml', 'rb') as f:
raw = f.read()
if raw.startswith(b'\xef\xbb\xbf'):
raw = raw[3:]
root = ET.fromstring(raw.decode('utf-8'))
Node.js 中 fs.readFileSync() 加载 XML 报错 Invalid character at position 0
Node.js 的 fs.readFileSync() 若指定 'utf8' 编码,会保留 BOM;而 libxmljs 或 fast-xml-parser 等库在解析字符串时,开头的 \ufeff 会被视为非法 XML 字符。
实操建议:
- 用
fs.readFileSync(path, null)读成 Buffer,再用toString('utf8')前手动截断 BOM - 或改用
fs.promises.readFile(path, 'utf8')+.replace(/^\uFEFF/, ''),更简洁但注意正则只清开头一个 BOM - 避免用
encoding: 'utf8-sig'(仅限fs.createReadStream配合某些解析器,不通用)
const buf = fs.readFileSync('data.xml');
const xmlStr = buf.toString('utf8').replace(/^\uFEFF/, '');
const doc = new DOMParser().parseFromString(xmlStr, 'text/xml');
Java 使用 DocumentBuilder.parse() 报 org.xml.sax.SAXParseException: Content is not allowed in prolog
这个错误几乎等于“XML 开头有不可见垃圾”,BOM 是最常见元凶。Java 的 InputStream 不会自动过滤 BOM,DocumentBuilder 又严格遵循 XML 规范,把 \ufeff 当作 prolog(文档序言)外的非法内容。
实操建议:
- 不要直接传
new FileInputStream()给parse();先包装成能跳过 BOM 的InputStream - 用
org.apache.commons.io.input.BOMInputStream(Apache Commons IO)是最稳妥的方案,它能自动识别并跳过 UTF-8/UTF-16 BOM - 若不想加依赖,可手写一个简单装饰器:读前 3 字节,匹配
0xEF 0xBB 0xBF后丢弃,再把剩余流交给解析器
try (InputStream is = new BOMInputStream(new FileInputStream("data.xml"))) {
Document doc = builder.parse(is);
}
为什么有些编辑器“保存为 UTF-8”仍带 BOM?怎么确认文件真有 BOM?
BOM 不是编码决定的,是编辑器“保存行为”决定的。VS Code 默认不加 BOM,但 Notepad++、旧版 Sublime、Windows 记事本默认加;而且“UTF-8 with BOM”和“UTF-8”在很多编辑器里是两个独立选项。
确认方法比猜靠谱:
- Linux/macOS 下用
head -c 5 data.xml | hexdump -C,看到ef bb bf就是 UTF-8 BOM - Windows 下用
certutil -hashfile data.xml SHA1不行,得用Format-Hex data.xml(PowerShell),看前几字节 - Python 里直接
open('x.xml','rb').read(3) == b'\xef\xbb\xbf'最直白
BOM 本身不破坏文本可读性,但 XML 处理链里任何一环没处理它,就会在某个看似无关的位置突然崩掉——尤其当 XML 被拼接、缓存、代理转发后,BOM 更容易被忽略。









