XML解析 - PHP编码

5
我有一个很大的XML文件(>15Mb),我需要读取它,解析它,并在数据库中存储一些值。我的问题是,XML以不同的格式(UTF-8,ISO-8859-1)出现。
对于UTF-8没有问题。但是ISO-8859-1给我带来了巨大的问题!!标签带有特殊字符,XMLReader和readOuterXML()无法正确解析。
我已经尝试过使用,但没有成功。
$xml = new XMLReader;
$xml->open($import_file,'ISO-8859-1');  

尝试过:

  • utf8_encode
  • mb_convert_encoding($stringXML,'UTF-8');
  • iconv("ISO-8859-1","UTF-8//TRANSLIT",$stringXML);

XML(简化)

  • tag (id) --> 没有问题
  • tag (baños) --> 有问题

xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
    <id><![CDATA[5531]]></id>
    <baños><![CDATA[0]]></baños>
</data>

它们都没有帮助到我。


展示实际的XML数据。尝试复制粘贴它,然后还要提供无法解析的实际字符的十六进制转储。这将有助于确定问题所在。 - Evert
@XML已上传!谢谢。 - Nacho
好的,这里是重要的部分!在命令行上使用 hex-dump -C(或其他十六进制编辑器),找出用于 ñ 字符的字节值。我们想确保它实际上是 ISO-8859-1 而不是其他东西。 - Evert
hexdump 会自动将任何非 ASCII 字符更改为 .。重要的部分在该行的前半部分,即实际代码。在那里,你的 ñ 被更改为 0x96。0x96 不是 ISO-8859-1 中的有效字符代码,也不是 CP-1252 中的有效字符代码。因此,无论你的编码是什么,它都是其他编码! - Evert
@Evert 希望事情能够变得那么简单!但是改变那个是不可能的... - Nacho
显示剩余6条评论
5个回答

0
正如 @Evert 指出的那样,你的 ñ 的字节码是:0x96,而你的 XML 文件的编码实际上是MacRoman在这里查看表格)。
如果你想将数据转换为 UTF-8 格式,这是你需要做的:
$stringXML = file_get_contents('yourFile.xml');
$data = iconv('MACINTOSH', 'UTF-8', $stringXML);

另一种可能性是使用命令行的iconv:

iconv -f MACINTOSH -t UTF-8 file.xml > outputUTF8.xml

(这里是 Linux 版本的库的链接:http://www.gnu.org/software/libiconv/

它将<num_baños>转换为<num_baÒos>。 - Nacho
你如何获取XML文档的内容?你应该使用fopen而不是XMLReader - Tip-Sy
我使用 @file_get_contents('myurl/file.xml'); 来获取它。 - Nacho
我必须在 PHP 服务器中使用这个。 - Nacho
我明白了。以防万一,尝试使用PHP中的“iconv”函数,用“MACROMAN”代替“macintosh”,我们永远不知道,它可能会起作用... - Tip-Sy
显示剩余2条评论

0

我成功地使用Symfony的XmlEncoder类(https://github.com/symfony/Serializer)解码了给定的xml。我将xml存储在test.xml文件中,以确保正确的编码(因为我的php文件默认情况下是UTF-8编码)。

$encoder = new Symfony\Component\Serializer\Encoder\XmlEncoder();
$data = $encoder->decode(file_get_contents('test.xml'), 'xml');
//$data = ['id' = 5531, 'baños' => 0]

0
如果XML标签中存在特殊字符的问题,这里有一种快速简单的方法可以在解析之前清理标签:
$xml = <<<END
<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
    <id><![CDATA[5531]]></id>
    <baños><![CDATA[0]]></baños>
</data>
END;

function FilterXML($matches)
{
  return $matches[1] . preg_replace('/[^a-z]/ui', '_', $matches[2]) .
    $matches[3];
}

var_dump(preg_replace_callback('#(</?)([^!?]+?)(\\s|>)#', 'FilterXML', $xml));

它将用<ba_os>替换<baños>


0

PHP 中的内部编码是什么?您可以使用 echo mb_internal_encoding(); 进行检查。

如果它是 UTF-8,则 mb_convert_encoding($data, "UTF-8") 不会做任何事情,因为第三个参数 $from_encoding 已经是 "UTF-8"。

您必须将源编码作为第三个参数提供给该函数。

所以也许这样就可以解决问题:

//check which encoding the data has? 
$encoding = mb_detect_encoding($data);
if($encoding != "UTF-8"){
    //specify from which encoding to convert to utf-8
    $data = mb_convert_encoding($data, "UTF-8", $encoding); 
}

mb_detect_encoding 的问题在于它不支持 MacRoman(这个 XML 文件的实际编码)。支持的编码完整列表可以在这里找到。 - Tip-Sy
@Tip-Sy,感谢您的提示,我之前不知道这个。所以如果要用纯PHP实现,看起来你必须自己实现它。这里有一个示例实现:http://ctd-web.fr/blog/2011/02/18/php-detection-encodage-mac-roman-utf-8/。 - Max

-1

您可以先尝试读取XML文件,然后转换特殊字符,最后使用XMLReader读取XML字符串。

以下是代码:

<?php
header("Content-Type: text/plain; charset=ISO-8859-1");
function normalizeChars($s){
    $replace = array(
        '&amp;' => 'and', '@' => 'at', '©' => 'c', '®' => 'r', 'À' => 'a',
        'Á' => 'a', 'Â' => 'a', 'Ä' => 'a', 'Å' => 'a', 'Æ' => 'ae','Ç' => 'c',
        'È' => 'e', 'É' => 'e', 'Ë' => 'e', 'Ì' => 'i', 'Í' => 'i', 'Î' => 'i',
        'Ï' => 'i', 'Ò' => 'o', 'Ó' => 'o', 'Ô' => 'o', 'Õ' => 'o', 'Ö' => 'o',
        'Ø' => 'o', 'Ù' => 'u', 'Ú' => 'u', 'Û' => 'u', 'Ü' => 'u', 'Ý' => 'y',
        'ß' => 'ss','à' => 'a', 'á' => 'a', 'â' => 'a', 'ä' => 'a', 'å' => 'a',
        'æ' => 'ae','ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
        'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ò' => 'o', 'ó' => 'o',
        'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u',
        'û' => 'u', 'ü' => 'u', 'ý' => 'y', 'þ' => 'p', 'ÿ' => 'y', 'Ā' => 'a',
        'ā' => 'a', 'Ă' => 'a', 'ă' => 'a', 'Ą' => 'a', 'ą' => 'a', 'Ć' => 'c',
        'ć' => 'c', 'Ĉ' => 'c', 'ĉ' => 'c', 'Ċ' => 'c', 'ċ' => 'c', 'Č' => 'c',
        'č' => 'c', 'Ď' => 'd', 'ď' => 'd', 'Đ' => 'd', 'đ' => 'd', 'Ē' => 'e',
        'ē' => 'e', 'Ĕ' => 'e', 'ĕ' => 'e', 'Ė' => 'e', 'ė' => 'e', 'Ę' => 'e',
        'ę' => 'e', 'Ě' => 'e', 'ě' => 'e', 'Ĝ' => 'g', 'ĝ' => 'g', 'Ğ' => 'g',
        'ğ' => 'g', 'Ġ' => 'g', 'ġ' => 'g', 'Ģ' => 'g', 'ģ' => 'g', 'Ĥ' => 'h',
        'ĥ' => 'h', 'Ħ' => 'h', 'ħ' => 'h', 'Ĩ' => 'i', 'ĩ' => 'i', 'Ī' => 'i',
        'ī' => 'i', 'Ĭ' => 'i', 'ĭ' => 'i', 'Į' => 'i', 'į' => 'i', 'İ' => 'i',
        'ı' => 'i', 'IJ' => 'ij','ij' => 'ij','Ĵ' => 'j', 'ĵ' => 'j', 'Ķ' => 'k',
        'ķ' => 'k', 'ĸ' => 'k', 'Ĺ' => 'l', 'ĺ' => 'l', 'Ļ' => 'l', 'ļ' => 'l',
        'Ľ' => 'l', 'ľ' => 'l', 'Ŀ' => 'l', 'ŀ' => 'l', 'Ł' => 'l', 'ł' => 'l',
        'Ń' => 'n', 'ń' => 'n', 'Ņ' => 'n', 'ņ' => 'n', 'Ň' => 'n', 'ň' => 'n',
        'ʼn' => 'n', 'Ŋ' => 'n', 'ŋ' => 'n', 'Ō' => 'o', 'ō' => 'o', 'Ŏ' => 'o',
        'ŏ' => 'o', 'Ő' => 'o', 'ő' => 'o', 'Œ' => 'oe','œ' => 'oe','Ŕ' => 'r',
        'ŕ' => 'r', 'Ŗ' => 'r', 'ŗ' => 'r', 'Ř' => 'r', 'ř' => 'r', 'Ś' => 's',
        'ś' => 's', 'Ŝ' => 's', 'ŝ' => 's', 'Ş' => 's', 'ş' => 's', 'Š' => 's',
        'š' => 's', 'Ţ' => 't', 'ţ' => 't', 'Ť' => 't', 'ť' => 't', 'Ŧ' => 't',
        'ŧ' => 't', 'Ũ' => 'u', 'ũ' => 'u', 'Ū' => 'u', 'ū' => 'u', 'Ŭ' => 'u',
        'ŭ' => 'u', 'Ů' => 'u', 'ů' => 'u', 'Ű' => 'u', 'ű' => 'u', 'Ų' => 'u',
        'ų' => 'u', 'Ŵ' => 'w', 'ŵ' => 'w', 'Ŷ' => 'y', 'ŷ' => 'y', 'Ÿ' => 'y',
        'Ź' => 'z', 'ź' => 'z', 'Ż' => 'z', 'ż' => 'z', 'Ž' => 'z', 'ž' => 'z',
        'ſ' => 'z', 'Ə' => 'e', 'ƒ' => 'f', 'Ơ' => 'o', 'ơ' => 'o', 'Ư' => 'u',
        'ư' => 'u', 'Ǎ' => 'a', 'ǎ' => 'a', 'Ǐ' => 'i', 'ǐ' => 'i', 'Ǒ' => 'o',
        'ǒ' => 'o', 'Ǔ' => 'u', 'ǔ' => 'u', 'Ǖ' => 'u', 'ǖ' => 'u', 'Ǘ' => 'u',
        'ǘ' => 'u', 'Ǚ' => 'u', 'ǚ' => 'u', 'Ǜ' => 'u', 'ǜ' => 'u', 'Ǻ' => 'a',
        'ǻ' => 'a', 'Ǽ' => 'ae','ǽ' => 'ae','Ǿ' => 'o', 'ǿ' => 'o', 'ə' => 'e',
        'Ё' => 'jo','Є' => 'e', 'І' => 'i', 'Ї' => 'i', 'А' => 'a', 'Б' => 'b',
        'В' => 'v', 'Г' => 'g', 'Д' => 'd', 'Е' => 'e', 'Ж' => 'zh','З' => 'z',
        'И' => 'i', 'Й' => 'j', 'К' => 'k', 'Л' => 'l', 'М' => 'm', 'Н' => 'n',
        'О' => 'o', 'П' => 'p', 'Р' => 'r', 'С' => 's', 'Т' => 't', 'У' => 'u',
        'Ф' => 'f', 'Х' => 'h', 'Ц' => 'c', 'Ч' => 'ch','Ш' => 'sh','Щ' => 'sch',
        'Ъ' => '-', 'Ы' => 'y', 'Ь' => '-', 'Э' => 'je','Ю' => 'ju','Я' => 'ja',
        'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e',
        'ж' => 'zh','з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l',
        'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's',
        'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch',
        'ш' => 'sh','щ' => 'sch','ъ' => '-','ы' => 'y', 'ь' => '-', 'э' => 'je',
        'ю' => 'ju','я' => 'ja','ё' => 'jo','є' => 'e', 'і' => 'i', 'ї' => 'i',
        'Ґ' => 'g', 'ґ' => 'g', 'א' => 'a', 'ב' => 'b', 'ג' => 'g', 'ד' => 'd',
        'ה' => 'h', 'ו' => 'v', 'ז' => 'z', 'ח' => 'h', 'ט' => 't', 'י' => 'i',
        'ך' => 'k', 'כ' => 'k', 'ל' => 'l', 'ם' => 'm', 'מ' => 'm', 'ן' => 'n',
        'נ' => 'n', 'ס' => 's', 'ע' => 'e', 'ף' => 'p', 'פ' => 'p', 'ץ' => 'C',
        'צ' => 'c', 'ק' => 'q', 'ר' => 'r', 'ש' => 'w', 'ת' => 't', '™' => 'tm',
        'ñ' => 'n',
    );
    return strtr($s, $replace);
}

$path_to_file = '';
$xml_text = @file_get_contents($path_to_file);
if(!empty($xml_text)){
    $xml_text = normalizeChars($xml_text);
    $xml = new XMLReader();
    $xml->XML($xml_text);
}
?>

另外,如果你在追求性能的话,你可以尝试一下SimpleXML和DOM Document,正如以下StackOverflow问题所提到的:https://dev59.com/jHI-5IYBdhLWcg3weoPR#1835324

编辑:

我添加了header("Content-Type: text/plain; charset=ISO-8859-1"),因为strtr只适用于ISO-8859-1。 我已经用OP提供的XML字符串尝试过了,它完全正常工作。如果有任何缺失的字符,请随意将其添加到数组中。


我是这样做的:但是没有替换字符... $content = @file_get_contents("www.path.com/file.xml");
$content = $this->normalizeChars($content); echo "替换完成!<br>"; $newxml = simplexml_load_string($content); print_r($newxml);
- Nacho
很高兴它起作用了。如果您认为它解决了您的问题,请投票支持答案。 - Wissam El-Kik
不行。@Wissam El-Kik 我写了“不替换字符”。这并没有解决我的问题。 - Nacho
@user2855036 我刚意识到 strtr 需要一个 ISO-8859-1 字符集。现在应该可以工作了。 - Wissam El-Kik
1
这是一种修改源代码的hack,通常不能解决底层编码问题。你应该使用iconv而不是试图重新发明轮子。为什么“ת”应该映射到“t”,而不是“n”?如果你这样做,就没有语义了。 - Phil
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接