Как пропустить недопустимые символы в файле XML с помощью PHP - PullRequest
21 голосов
/ 12 августа 2010

Я пытаюсь проанализировать XML-файл с помощью PHP, но получаю сообщение об ошибке:

Ошибка синтаксического анализатора: Char 0x0 вне допустимого диапазона в

Я думаю, что это из-за содержания XML, я думаю, что есть специальный символ "☆", какие-нибудь идеи, что я могу сделать, чтобы это исправить?

Я также получаю:

ошибка синтаксического анализатора: преждевременное завершение данных в строке элемента тега

Что может быть причиной этой ошибки?

Я использую simplexml_load_file.

Обновление:

Я пытаюсь найти строку ошибки и вставить ее содержимое в виде одного файла XML, и это может работать !! поэтому я до сих пор не могу понять, что делает сбой анализа XML-файла. PS Это огромный XML-файл размером более 100 Мб, будет ли он обрабатывать ошибку?

Ответы [ 7 ]

31 голосов
/ 12 августа 2010

У вас есть контроль над 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;
}
13 голосов
/ 04 апреля 2014

Я решил проверить все значения UTF-8 (0-1114111), чтобы убедиться, что все работает так, как должно. Использование preg_replace () вызывает возврат значения NULL из-за ошибок при тестировании всех значений utf-8. Это решение, которое я придумала.

$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 голосов
/ 12 августа 2010

Если вы контролируете данные, убедитесь, что они закодированы правильно (т. Е. В той кодировке, которую вы обещали в теге xml, например, если у вас есть:

<?xml version="1.0" encoding="UTF-8"?>

, тогда вам нужно убедиться, что ваши данные в формате UTF-8.

Если у вас нет контроля над данными, кричите на тех, кто это делает.

Вы можете использовать такой инструмент, как xmllint , чтобы проверить, какие части данных недействительны.

1 голос
/ 03 марта 2015

Моя проблема была "&" символ (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;
}
0 голосов
/ 31 июля 2015

Не решение php, но оно работает:

Скачать Notepad ++ https://notepad -plus-plus.org /

Откройте файл .xml в Notepad ++

Из главного меню: Поиск -> Режим поиска установите это значение: Расширенный

Затем

Заменить -> Найти что \ x00;Заменить на {оставить пустым}

Затем заменить на все

Роб

0 голосов
/ 11 ноября 2011

Для неразрушающего метода загрузки этого типа ввода в SimpleXMLElement, см. Мой ответ на Как обрабатывать недопустимый Unicode с Simplexml

0 голосов
/ 12 августа 2010

Убедитесь, что ваш источник XML действителен.Смотри http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...