我正在尝试使用PHP解析XML文件,但是收到错误消息:
解析器错误:字符0x0超出
中允许的范围
我认为这是因为XML的内容,我认为有一个特殊符号“☆”,任何想法我能做些什么来解决它?
我也得到:
解析器错误:标记项目行中数据的过早结束
可能导致该错误的原因是什么?
我正在使用simplexml_load_file
。
我尝试找到错误行并将其内容粘贴为单个xml文件,它可以正常工作!!所以我仍然无法弄清楚是什么让xml文件解析失败。 PS它是一个超过100M的巨大xml文件,它会解析错误吗?
答案 0 :(得分:31)
您是否可以控制XML?如果是,请确保数据包含在<![CDATA[
.. ]]>
块中。
您还需要清除无效字符:
/**
* Removes invalid XML
*
* @access public
* @param string $value
* @return string
*/
function stripInvalidXml($value)
{
$ret = "";
$current;
if (empty($value))
{
return $ret;
}
$length = strlen($value);
for ($i=0; $i < $length; $i++)
{
$current = ord($value{$i});
if (($current == 0x9) ||
($current == 0xA) ||
($current == 0xD) ||
(($current >= 0x20) && ($current <= 0xD7FF)) ||
(($current >= 0xE000) && ($current <= 0xFFFD)) ||
(($current >= 0x10000) && ($current <= 0x10FFFF)))
{
$ret .= chr($current);
}
else
{
$ret .= " ";
}
}
return $ret;
}
答案 1 :(得分:12)
我决定测试所有UTF-8值(0-1114111),以确保工作正常。使用preg_replace()会导致在测试所有utf-8值时由于错误而返回NULL。这是我提出的解决方案。
$utf_8_range = range(0, 1114111);
$output = ords_to_utfstring($utf_8_range);
$sanitized = sanitize_for_xml($output);
/**
* Removes invalid XML
*
* @access public
* @param string $value
* @return string
*/
function sanitize_for_xml($input) {
// Convert input to UTF-8.
$old_setting = ini_set('mbstring.substitute_character', '"none"');
$input = mb_convert_encoding($input, 'UTF-8', 'auto');
ini_set('mbstring.substitute_character', $old_setting);
// Use fast preg_replace. If failure, use slower chr => int => chr conversion.
$output = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', '', $input);
if (is_null($output)) {
// Convert to ints.
// Convert ints back into a string.
$output = ords_to_utfstring(utfstring_to_ords($input), TRUE);
}
return $output;
}
/**
* Given a UTF-8 string, output an array of ordinal values.
*
* @param string $input
* UTF-8 string.
* @param string $encoding
* Defaults to UTF-8.
*
* @return array
* Array of ordinal values representing the input string.
*/
function utfstring_to_ords($input, $encoding = 'UTF-8'){
// Turn a string of unicode characters into UCS-4BE, which is a Unicode
// encoding that stores each character as a 4 byte integer. This accounts for
// the "UCS-4"; the "BE" prefix indicates that the integers are stored in
// big-endian order. The reason for this encoding is that each character is a
// fixed size, making iterating over the string simpler.
$input = mb_convert_encoding($input, "UCS-4BE", $encoding);
// Visit each unicode character.
$ords = array();
for ($i = 0; $i < mb_strlen($input, "UCS-4BE"); $i++) {
// Now we have 4 bytes. Find their total numeric value.
$s2 = mb_substr($input, $i, 1, "UCS-4BE");
$val = unpack("N", $s2);
$ords[] = $val[1];
}
return $ords;
}
/**
* Given an array of ints representing Unicode chars, outputs a UTF-8 string.
*
* @param array $ords
* Array of integers representing Unicode characters.
* @param bool $scrub_XML
* Set to TRUE to remove non valid XML characters.
*
* @return string
* UTF-8 String.
*/
function ords_to_utfstring($ords, $scrub_XML = FALSE) {
$output = '';
foreach ($ords as $ord) {
// 0: Negative numbers.
// 55296 - 57343: Surrogate Range.
// 65279: BOM (byte order mark).
// 1114111: Out of range.
if ( $ord < 0
|| ($ord >= 0xD800 && $ord <= 0xDFFF)
|| $ord == 0xFEFF
|| $ord > 0x10ffff) {
// Skip non valid UTF-8 values.
continue;
}
// 9: Anything Below 9.
// 11: Vertical Tab.
// 12: Form Feed.
// 14-31: Unprintable control codes.
// 65534, 65535: Unicode noncharacters.
elseif ($scrub_XML && (
$ord < 0x9
|| $ord == 0xB
|| $ord == 0xC
|| ($ord > 0xD && $ord < 0x20)
|| $ord == 0xFFFE
|| $ord == 0xFFFF
)) {
// Skip non valid XML values.
continue;
}
// 127: 1 Byte char.
elseif ( $ord <= 0x007f) {
$output .= chr($ord);
continue;
}
// 2047: 2 Byte char.
elseif ($ord <= 0x07ff) {
$output .= chr(0xc0 | ($ord >> 6));
$output .= chr(0x80 | ($ord & 0x003f));
continue;
}
// 65535: 3 Byte char.
elseif ($ord <= 0xffff) {
$output .= chr(0xe0 | ($ord >> 12));
$output .= chr(0x80 | (($ord >> 6) & 0x003f));
$output .= chr(0x80 | ($ord & 0x003f));
continue;
}
// 1114111: 4 Byte char.
elseif ($ord <= 0x10ffff) {
$output .= chr(0xf0 | ($ord >> 18));
$output .= chr(0x80 | (($ord >> 12) & 0x3f));
$output .= chr(0x80 | (($ord >> 6) & 0x3f));
$output .= chr(0x80 | ($ord & 0x3f));
continue;
}
}
return $output;
}
并在一个简单的对象或数组上执行此操作
// Recursive sanitize_for_xml.
function recursive_sanitize_for_xml(&$input){
if (is_null($input) || is_bool($input) || is_numeric($input)) {
return;
}
if (!is_array($input) && !is_object($input)) {
$input = sanitize_for_xml($input);
}
else {
foreach ($input as &$value) {
recursive_sanitize_for_xml($value);
}
}
}
答案 2 :(得分:2)
如果您可以控制数据,请确保它已正确编码(即,您使用xml标记中承诺的编码,例如,如果您有:
<?xml version="1.0" encoding="UTF-8"?>
然后你需要确保你的数据是UTF-8。
如果您无法控制数据,请对那些数据进行控制。
您可以使用xmllint之类的工具来检查数据的哪个部分无效。
答案 3 :(得分:1)
我的问题是“&amp;”字符(HEX 0x24),我改为:
function stripInvalidXml($value)
{
$ret = "";
$current;
if (empty($value))
{
return $ret;
}
$length = strlen($value);
for ($i=0; $i < $length; $i++)
{
$current = ord($value{$i});
if (($current == 0x9) ||
($current == 0xA) ||
($current == 0xD) ||
(($current >= 0x28) && ($current <= 0xD7FF)) ||
(($current >= 0xE000) && ($current <= 0xFFFD)) ||
(($current >= 0x10000) && ($current <= 0x10FFFF)))
{
$ret .= chr($current);
}
else
{
$ret .= " ";
}
}
return $ret;
}
答案 4 :(得分:1)
某些 Unicode 字符 must not appear in XML 1.0:
但在实践中,您经常不得不处理从包含此类字符的其他来源粗心生成的 XML。如果您想在 UTF-8 编码字符串中处理这种无效 XML 的特殊情况,我建议:
$str = preg_replace(
'/[\x00-\x08\x0B\x0C\x0E-\x1F]|\xED[\xA0-\xBF].|\xEF\xBF[\xBE\xBF]/',
"\xEF\xBF\xBD",
$str
);
这不使用 u
Unicode 正则表达式修饰符,而是直接在 UTF-8 编码字节上工作以提高性能。该模式的部分是:
[\x00-\x08\x0B\x0C\x0E-\x1F]
\xED[\xA0-\xBF].
\xEF\xBF[\xBE\xBF]
无效字符被替换为替换字符 U+FFFD (�),而不是简单地剥离它们。这使得诊断无效字符变得更加容易,甚至可以prevent security issues。
答案 5 :(得分:0)
答案 6 :(得分:0)
对于将此类输入加载到SimpleXMLElement中的非破坏性方法,请参阅How to handle invalid unicode with simplexml上的答案
答案 7 :(得分:-1)
不是php解决方案,但是,它有效:
下载Notepad ++ https://notepad-plus-plus.org/
在Notepad ++中打开.xml文件
从主菜单:搜索 - &gt; 搜索模式将其设置为:扩展
然后,
替换 - &gt;找到什么\ x00;替换为{leave empty}
然后,Replace_All
罗布