最稳妥读取xml文件应使用xml.etree.elementtree.parse()而非fromstring(),因其自动处理编码检测与文件打开;全树搜索节点用iter()而非findall();取文本需判空并strip();保存时须指定encoding='utf-8'和xml_declaration=true。

用 xml.etree.ElementTree.parse() 读取文件最稳妥
直接用 ElementTree 解析 XML 文件,首选 parse(),不是 fromstring()。后者只吃字符串,容易因编码问题报 UnicodeDecodeError 或解析失败;而 parse() 自动处理文件打开和编码检测(默认按系统 locale 或 XML 声明推断)。
常见错误:用 open('file.xml').read() 再喂给 fromstring(),结果中文乱码或开头有 BOM 时直接抛 ParseError: not well-formed。
- 正确做法:
tree = ET.parse('config.xml'),然后root = tree.getroot() - 如果必须从字符串加载,确保传入的是
str(不是bytes),且 XML 声明里的 encoding 和实际一致,比如<?xml version="1.0" encoding="utf-8"?> - Windows 上路径含中文?用原始字符串或正斜杠:
r'C:\data\test.xml'或'C:/data/test.xml',避免反斜杠转义出错
iter() 比 findall() 更适合深度遍历
要找所有同名节点(比如全部 <item></item>),别只依赖 findall('item') —— 它只查**直接子节点**,嵌套三层就漏了。真正“全树搜索”得用 iter()。
使用场景:配置文件里 <section></section> 套 <group></group> 再套 <param>,你想一次性提取所有 <param>。
立即学习“Python免费学习笔记(深入)”;
-
root.iter('param')返回生成器,遍历整个树中所有名为param的元素 -
root.findall('.//param')也能做到,但 XPath 语法在简单需求下显得冗余,且部分老版本 Python 对.//支持不稳定 - 注意:空元素(如
<flag></flag>)也会被iter()捕获,访问其text是None,别直接print(elem.text.strip()),先判空
取文本内容时,elem.text 和 elem.tail 得分开看
XML 里换行缩进也是内容 —— 它们不会消失,而是落在 text 或 tail 里。新手常以为 elem.text 就是“标签中间的字”,结果打印出来一堆空白或换行。
示例:<name>\n Alice\n</name> 中,elem.text == '\n Alice\n';而 <name>Bob</name><age>25</age> 中,name 元素的 tail 是 None,但若写成 <name>Bob</name> years old,则 name.tail == ' years old'。
- 安全取纯文本:
elem.text.strip() if elem.text else '' - 需要保留结构化空白?别用
strip(),改用re.sub(r'\s+', ' ', elem.text).strip() -
tail一般不用主动处理,除非你在拼接段落或还原文档格式
修改后保存 XML,记得用 tree.write() 并指定 encoding 和 xml_declaration
改完节点内容或增删元素后,直接 tree.write('out.xml') 很可能输出无声明、ASCII 编码、中文变问号的文件。
性能影响:不加 encoding='utf-8' 时,默认用系统编码(Windows 上常是 cp936),Linux 上可能崩;不加 xml_declaration=True,某些解析器会拒绝读取(尤其 Java 生态)。
- 推荐写法:
tree.write('out.xml', encoding='utf-8', xml_declaration=True) - 如果原文件有 DTD 或注释,
ElementTree默认不保留 —— 这是设计限制,别指望它做完整 round-trip - 想最小化格式变化?加
short_empty_elements=False(Python 3.9+),避免把<tag></tag>改成<tag></tag>
text 变 None、让中文输出成乱码、让下游系统拒收文件。










