PHP XMLReader 解析器错误:xmlParseCharRef:无效的 xmlChar 值

PHP XMLReader parser error : xmlParseCharRef: invalid xmlChar value

提问人:Jenemj 提问时间:6/26/2023 最后编辑:Jenemj 更新时间:6/28/2023 访问量:235

问:

我正在解析一个非常大的 Xml 文件,所以我需要使用 PHP 的 XMLReader。 无法从源头修改它们。因此,它们必须按原样进行解析。 问题在于文档包含 html 字符“&#”,读者将其检测为无效。


        $reader = new XMLReader();
    
        if (!$reader->open($fileNamePath))//File xml
            {
            echo "Error opening file: $fileNamePath".PHP_EOL;
            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;
                    }
                //Do stuff here
                }
         }
    
         $reader->close();

我收到很多这样的消息:

PHP 警告:XMLReader::readOuterXml():myfile.xml:162:解析器错误:xmlParseCharRef:无效的 xmlChar 值 2... Errore Nodo AIUTO 字符串无法解析为 XML

显然,该文件包含序列 .

以下是一些导致错误的XML文件代码:

<AIUTO><BASE_GIURIDICA_NAZIONALE>Quadro riepilogativo delle misure a sostegno delle imprese attive nei settori agricolo, forestale, della pesca 
e acquacoltura ai sensi della Comunicazione della Commissione europea C (2020) 1863 final – “Quadro 
temporaneo per le misure di aiuto di Stato a sostegno dell’economia nell’attuale emergenza del COVID&#2;19” e successive modifiche e integrazioni</BASE_GIURIDICA_NAZIONALE></AIUTO>

我想将每个文件逐行解析为文本,并替换无效的序列。

但这有点棘手。 有人有更好的解决方案吗?

php xml 解析 xmlreader 无效字符

评论

0赞 RiggsFolly 6/26/2023
您能否提供一个最小、完整和可验证的示例

答:

-1赞 Alessandro 6/26/2023 #1

在那里有一个 xml 文件,我发现最好的解决方法是将字符串替换为什么都没有:

$xml= str_replace('YOUR STIRNG',NULL,$xml);

如果无法删除 xml 中的数据,可以尝试解析 xml,然后使用以下命令循环每个数据:

$xml= simplexml_load_file('file.xml');
foreach($xml as $object){
  your code...
}

评论

0赞 Jenemj 6/26/2023
我不能对整个文件使用 simplexml...它太大了。
0赞 Alessandro 6/26/2023
那么str_replace()呢?
0赞 Jenemj 6/28/2023
关键是,在我对错误的字符串执行任何操作之前,在 $reader->readOuterXML() 行上引发了错误。使用 str_replace() 的唯一方法是将 before 文件解析为文本。
0赞 Alessandro 6/28/2023
当然,您需要在传递给读取器之前修改字符串。将文本加载到变量中,然后执行并将结果传递给读取器。$string=file_get_contents($file)str_replace
0赞 Jenemj 6/29/2023
是的,但是由于与simpleXml相同的原因,file_get_contents()不能使用:我的XML很大。你的想法是对的,但我不得不一行一行地做同样的事情。
0赞 Casimir et Hippolyte 6/27/2023 #2

您可以做的是构建一个自定义流过滤器,您可以在其中继续进行所需的所有修复。这样,您可以继续使用 XMLReader 将文件作为流读取,而无需一次加载完整内容。

class fix_entities_filter extends php_user_filter
{
    function filter($in, $out, &$consumed, $closing): int
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = $this->fix($bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
    
    function fix($data)
    {
        return strtr($data, ['&#2;' => '&#x202f;']);
    }
}

stream_filter_register("fix_entities", "fix_entities_filter")
    or die("Failed to register filter");

$file = 'file.xml';
$fileNamePath = "/path/to/your/$file";
$path = "php://filter/read=fix_entities/resource=$fileNamePath";

$reader = new XMLReader();
    
if (!$reader->open($path)) {
    echo "Error opening file: $fileNamePath", PHP_EOL;
}

演示

您可以在 PHP 手册和“Josh Lockhart - O'Reilly 的现代 PHP”一书中找到有关流过滤器的更多信息。

评论

0赞 Jenemj 6/28/2023
在 PHP 中,低于 8 会抛出声明警告。但解决方案的真正问题是,它输出整个文件。千兆文本...并且需要大量时间来处理文件。
0赞 Casimir et Hippolyte 6/30/2023
@Jenemj:为了避免旧 PHP 版本的警告,请删除(这是 PHP 8 的强制性要求,以避免警告)。这个解决方案,与你似乎认为的相反,不加载整个文件,而只加载 8192 字节的“桶”,所以使用的内存将是荒谬的。: int
0赞 Jenemj 6/30/2023
我没有说这个解决方案加载整个文件,只是它输出它......
0赞 Casimir et Hippolyte 7/1/2023
@Jenemj:它不会“输出文件”,我在演示中为新创建的simpleXML对象添加了一个only,并且仅在演示中添加了一个only,以显示发生了什么(奇怪的是你没有注意到这一点)。此外,此解决方案的速度大约快 2 倍。var_dump()
0赞 Casimir et Hippolyte 7/1/2023
@Jenemj:在处理文件之前,您是否可以使用gzip压缩文件?
-1赞 Jenemj 6/28/2023 #3

现在等待一个更清洁的工作解决方案,我用了我的“肮脏的想法”。

我创建了一个临时 xml,逐行删除导致错误的序列。

这是有效的:

$fileNamePath = "/path/to/your/file.xml";
$fileNamePathTmp = "/path/to/your/tmp.xml"

$handle = fopen($fileNamePath, "r");
$handle2 = fopen($fileNamePathTmp, "w");
if ($handle) {
while (($line = fgets($handle)) !== false) {
    $line2=str_replace(array("&#2;","&#11;","&#16;","&#26;"),"",$line);
    fputs($handle2,$line2);
}

fclose($handle);
fclose($handle2);
}

$reader = new XMLReader();

if (!$reader->open($fileNamePathTmp))//File xml tmp
    {
    echo "Error opening file: $fileNamePath".PHP_EOL;
    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;
            }
        //Do stuff here
        }
 }

 $reader->close();
 unlink($fileNamePathTmp);//Remove the temp xml