提问人:Jenemj 提问时间:6/26/2023 最后编辑:Jenemj 更新时间:6/28/2023 访问量:235
PHP XMLReader 解析器错误:xmlParseCharRef:无效的 xmlChar 值
PHP XMLReader parser error : xmlParseCharRef: invalid xmlChar value
问:
我正在解析一个非常大的 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 COVID19” e successive modifiche e integrazioni</BASE_GIURIDICA_NAZIONALE></AIUTO>
我想将每个文件逐行解析为文本,并替换无效的序列。
但这有点棘手。 有人有更好的解决方案吗?
答:
-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, ['' => ' ']);
}
}
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("","","",""),"",$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
评论