PHP XMLReader解析含无效字符引用的大XML文件报错求助
解决方案
方案1:过滤无效字符后再解析节点
在获取节点的XML内容后,先清理掉XML规范不允许的字符或字符引用,再传入SimpleXMLElement解析。
实现代码
先定义清理函数,移除XML 1.0中禁止的字符:
function cleanInvalidXmlChars(string $xml): string { // XML 1.0允许的字符范围:#x9(制表符)、#xA(换行)、#xD(回车)、[#x20-#xD7FF]、[#xE000-#xFFFD]、[#x10000-#x10FFFF] return preg_replace('/[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-\x10FFFF]/u', '', $xml); }
修改原代码的节点解析逻辑:
while($reader->read()) { if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'AIUTO') { try { $input = $reader->readOuterXML(); // 先清理无效字符再解析 $cleanedInput = cleanInvalidXmlChars($input); $nodeAiuto = new SimpleXMLElement($cleanedInput); } catch(Exception $e) { echo "Error Node AIUTO ".$e->getMessage().PHP_EOL; continue; } // 后续业务逻辑 } }
该方案逻辑直接,无需修改源文件,适用于多数规模的XML文件。
方案2:使用PHP流过滤器(适配超大文件)
通过自定义流过滤器,在XMLReader读取文件的过程中自动过滤无效字符,无需手动处理每个节点,内存占用低,适合GB级超大文件。
实现代码
注册自定义流过滤器:
class InvalidXmlCharFilter extends php_user_filter { public function filter($in, $out, &$consumed, $closing): int { while ($bucket = stream_bucket_make_writeable($in)) { // 清理流中的无效字符 $bucket->data = preg_replace('/[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-\x10FFFF]/u', '', $bucket->data); $consumed += $bucket->datalen; stream_bucket_append($out, $bucket); } return PSFS_PASS_ON; } } // 注册过滤器到PHP流系统 stream_filter_register('xml_cleaner', InvalidXmlCharFilter::class);
修改XMLReader的文件读取方式,通过过滤流加载文件:
// 打开带过滤器的文件流 $stream = fopen('php://filter/read=xml_cleaner/resource=' . $fileNamePath, 'r'); $reader = new XMLReader(); if (!$reader->open($stream)) { echo "Error opening file: $fileNamePath".PHP_EOL; fclose($stream); continue; } echo "Processing file: $file".PHP_EOL; while($reader->read()) { if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'AIUTO') { try { $input = $reader->readOuterXML(); $nodeAiuto = new SimpleXMLElement($input); } catch(Exception $e) { echo "Error Node AIUTO ".$e->getMessage().PHP_EOL; continue; } // 后续业务逻辑 } } $reader->close(); fclose($stream);
该方案在流层面处理字符,无需加载整个文件到内存,性能更优。
方案3:调整libxml解析选项(谨慎使用)
通过设置libxml的错误抑制选项,允许解析非严格XML,但可能忽略其他潜在格式问题,仅适用于无效字符较少且XML整体结构规范的场景。
实现代码
// 启用libxml内部错误处理,抑制外部警告 libxml_use_internal_errors(true); $reader = new XMLReader(); // 设置解析选项:忽略空白、禁用实体扩展、抑制错误与警告 $reader->open($fileNamePath, null, LIBXML_NOBLANKS | LIBXML_NOENT | LIBXML_NOERROR | LIBXML_NOWARNING); echo "Processing file: $file".PHP_EOL; while($reader->read()) { if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'AIUTO') { try { $input = $reader->readOuterXML(); $nodeAiuto = new SimpleXMLElement($input, LIBXML_NOERROR | LIBXML_NOWARNING); } catch(Exception $e) { echo "Error Node AIUTO ".$e->getMessage().PHP_EOL; // 清空错误栈避免累积 libxml_clear_errors(); continue; } // 后续业务逻辑 } } $reader->close(); // 恢复默认错误处理机制 libxml_use_internal_errors(false);
内容的提问来源于stack exchange,提问作者Jenemj
相关产品推荐
相关产品推荐

