不能用正则解析 XML,因其无法处理 CDATA、实体引用、命名空间、嵌套标签等;仅当输入完全受控且无任何 XML 复杂特性时才可谨慎使用;推荐优先用 SimpleXML 并辅以异常兜底。

别用正则解析 XML,除非你明确知道它不是 XML——比如标签不闭合、嵌套错乱、混着 HTML 片段、或压根是“类 XML”的日志格式。一旦 XML 出现 CDATA、实体引用(&、<)、命名空间、属性值含引号嵌套,preg_match 就会漏匹配、错截断甚至崩溃。
为什么 preg_match 提取 XML 内容容易出错
正则不是解析器,它没有状态机、不跟踪标签层级、无法识别转义边界。常见翻车点:
-
/<name>(.*)<\/name>/会跨标签匹配,遇到<name>Alice</name><email>alice@example.com</email>时,$1可能捕获到Alice</name><email>alice@example.com -
[^<]+看似安全,但遇上<name>A&B</name>,&不是标签起始,却会被当成分隔符截断 - 属性值含双引号时,如
<user id="123" name="Alice "Dev"">,正则很难可靠提取name的完整值 - XML 声明(
<?xml version="1.0"?>)或注释(<!-- ... -->)会干扰模式定位
什么情况下可以谨慎用正则提取“类 XML”内容
仅限以下全部满足的场景:
- 输入源完全受控,确认无嵌套同名标签(例如每行一个
<item>,且内部无子<item>) - 不含 CDATA、处理指令、注释、命名空间、DOCTYPE
- 所有标签严格闭合,且文本内容中不含
<字符(或已确保被实体化为) - 只需提取少量字段,且可接受“偶尔失败后人工补救”
示例:解析电商导出的简易商品片段(无嵌套、无属性、无特殊字符)
立即学习“PHP免费学习笔记(深入)”;
$xml = '<product><sku>ABC123</sku><price>29.99</price></product>';
$pattern = '/<sku>([^<]*)<\/sku>/';
if (preg_match($pattern, $xml, $m)) {
$sku = $m[1]; // ABC123
}更稳的替代方案:SimpleXML + 异常兜底
哪怕 XML 格式有点毛刺,也优先走 simplexml_load_string(),配合错误抑制和 fallback:
- 用
@simplexml_load_string($xml)抑制警告,再检查返回值是否为false - 若失败,再降级用正则提取关键字段(此时已知结构简单,风险可控)
- 对可能为空的节点,统一用
(string)$node转换,避免 Notice - 涉及属性时,必须用
$node->attributes(),不能靠正则硬抠
示例:
$xml = '<item><name>Test</name><price currency="USD">19.99</price></item>';
$obj = @simplexml_load_string($xml);
if ($obj === false) {
// fallback to regex only for name & price text
} else {
$name = (string)$obj->name;
$price = (string)$obj->price;
$currency = (string)$obj->price['currency']; // 正确读属性
}真正棘手的是混合了 HTML 标签、JS 注释、未转义尖括号的“伪 XML”。这种数据不该叫 XML,该叫“带标签的文本”,正则只是权宜之计,长期应推动上游修正输出格式。



















