XML-анализ ошибок SOAP-полезная нагрузка - нет памяти [дубликат]

Вы можете использовать это:

grep -inr "Text" folder/to/be/searched/
50
задан JakeGould 1 October 2014 в 20:19
поделиться

5 ответов

Есть только два php API, которые действительно подходят для обработки больших файлов. Первый - старый expat api, а второй - более новые функции XMLreader . Эти apis читают непрерывные потоки, а не загружают все дерево в память (это то, что делают simplexml и DOM).

Например, вы можете посмотреть на этот частичный парсер каталога DMOZ:

<?php

class SimpleDMOZParser
{
    protected $_stack = array();
    protected $_file = "";
    protected $_parser = null;

    protected $_currentId = "";
    protected $_current = "";

    public function __construct($file)
    {
        $this->_file = $file;

        $this->_parser = xml_parser_create("UTF-8");
        xml_set_object($this->_parser, $this);
        xml_set_element_handler($this->_parser, "startTag", "endTag");
    }

    public function startTag($parser, $name, $attribs)
    {
        array_push($this->_stack, $this->_current);

        if ($name == "TOPIC" && count($attribs)) {
            $this->_currentId = $attribs["R:ID"];
        }

        if ($name == "LINK" && strpos($this->_currentId, "Top/Home/Consumer_Information/Electronics/") === 0) {
            echo $attribs["R:RESOURCE"] . "\n";
        }

        $this->_current = $name;
    }

    public function endTag($parser, $name)
    {
        $this->_current = array_pop($this->_stack);
    }

    public function parse()
    {
        $fh = fopen($this->_file, "r");
        if (!$fh) {
            die("Epic fail!\n");
        }

        while (!feof($fh)) {
            $data = fread($fh, 4096);
            xml_parse($this->_parser, $data, feof($fh));
        }
    }
}

$parser = new SimpleDMOZParser("content.rdf.u8");
$parser->parse();
76
ответ дан Emil H 22 August 2018 в 14:33
поделиться
  • 1
    Наиболее определенно лучший ответ – Evert 26 May 2009 в 22:27
  • 2
    Это отличный ответ, но мне потребовалось много времени, чтобы понять, что вам нужно использовать xml_set_default_handler () для доступа к данным узла XML, причем приведенный выше код вы можете видеть только имя узлов и их атрибутов. – DirtyBirdNJ 18 January 2012 в 19:53

Это очень похожий вопрос на Лучший способ обработки больших XML в PHP , но с очень хорошим конкретным ответом, направленным на устранение конкретной проблемы разбора каталога DMOZ. Однако, поскольку это хороший хит Google для больших XML-файлов в целом, я также отправлю ответ на другой вопрос:

. Я беру его на себя:

https : //github.com/prewk/XmlStreamer

Простой класс, который будет извлекать всех детей в корневой элемент XML при потоковой передаче файла. Протестировано на 108 MB XML-файле из pubmed.com.

class SimpleXmlStreamer extends XmlStreamer {
    public function processNode($xmlString, $elementName, $nodeIndex) {
        $xml = simplexml_load_string($xmlString);

        // Do something with your SimpleXML object

        return true;
    }
}

$streamer = new SimpleXmlStreamer("myLargeXmlFile.xml");
$streamer->parse();
12
ответ дан Community 22 August 2018 в 14:33
поделиться
  • 1
    Отлично! Благодарю. один вопрос: как получить атрибут корневого узла, используя это? – gyaani_guy 15 October 2013 в 11:35
  • 2
    @gyaani_guy Я не думаю, что в настоящее время это возможно, к сожалению. – oskarth 22 December 2013 в 23:53
  • 3
    Это просто загружает весь файл в память! – Nick Strupat 7 March 2014 в 18:14
  • 4
    @NickStrupat Неправильно, метод processNode выполняется один раз на узел. Поэтому только один узел находится в памяти в любой момент времени. Символ simplexml_load_string в коде относится только к узлу xml, а не ко всему XML-документу. – oskarth 31 March 2014 в 17:03

Мне недавно пришлось разбирать некоторые довольно большие XML-документы и нужен метод для чтения одного элемента за раз.

Если у вас есть следующий файл complex-test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Complex>
  <Object>
    <Title>Title 1</Title>
    <Name>It's name goes here</Name>
    <ObjectData>
      <Info1></Info1>
      <Info2></Info2>
      <Info3></Info3>
      <Info4></Info4>
    </ObjectData>
    <Date></Date>
  </Object>
  <Object></Object>
  <Object>
    <AnotherObject></AnotherObject>
    <Data></Data>
  </Object>
  <Object></Object>
  <Object></Object>
</Complex>

И хотел вернуть <Object/> s

PHP:

require_once('class.chunk.php');

$file = new Chunk('complex-test.xml', array('element' => 'Object'));

while ($xml = $file->read()) {
  $obj = simplexml_load_string($xml);
  // do some parsing, insert to DB whatever
}

###########
Class File
###########

<?php
/**
 * Chunk
 * 
 * Reads a large file in as chunks for easier parsing.
 * 
 * The chunks returned are whole <$this->options['element']/>s found within file.
 * 
 * Each call to read() returns the whole element including start and end tags.
 * 
 * Tested with a 1.8MB file, extracted 500 elements in 0.11s
 * (with no work done, just extracting the elements)
 * 
 * Usage:
 * <code>
 *   // initialize the object
 *   $file = new Chunk('chunk-test.xml', array('element' => 'Chunk'));
 *   
 *   // loop through the file until all lines are read
 *   while ($xml = $file->read()) {
 *     // do whatever you want with the string
 *     $o = simplexml_load_string($xml);
 *   }
 * </code>
 * 
 * @package default
 * @author Dom Hastings
 */
class Chunk {
  /**
   * options
   *
   * @var array Contains all major options
   * @access public
   */
  public $options = array(
    'path' => './',       // string The path to check for $file in
    'element' => '',      // string The XML element to return
    'chunkSize' => 512    // integer The amount of bytes to retrieve in each chunk
  );

  /**
   * file
   *
   * @var string The filename being read
   * @access public
   */
  public $file = '';
  /**
   * pointer
   *
   * @var integer The current position the file is being read from
   * @access public
   */
  public $pointer = 0;

  /**
   * handle
   *
   * @var resource The fopen() resource
   * @access private
   */
  private $handle = null;
  /**
   * reading
   *
   * @var boolean Whether the script is currently reading the file
   * @access private
   */
  private $reading = false;
  /**
   * readBuffer
   * 
   * @var string Used to make sure start tags aren't missed
   * @access private
   */
  private $readBuffer = '';

  /**
   * __construct
   * 
   * Builds the Chunk object
   *
   * @param string $file The filename to work with
   * @param array $options The options with which to parse the file
   * @author Dom Hastings
   * @access public
   */
  public function __construct($file, $options = array()) {
    // merge the options together
    $this->options = array_merge($this->options, (is_array($options) ? $options : array()));

    // check that the path ends with a /
    if (substr($this->options['path'], -1) != '/') {
      $this->options['path'] .= '/';
    }

    // normalize the filename
    $file = basename($file);

    // make sure chunkSize is an int
    $this->options['chunkSize'] = intval($this->options['chunkSize']);

    // check it's valid
    if ($this->options['chunkSize'] < 64) {
      $this->options['chunkSize'] = 512;
    }

    // set the filename
    $this->file = realpath($this->options['path'].$file);

    // check the file exists
    if (!file_exists($this->file)) {
      throw new Exception('Cannot load file: '.$this->file);
    }

    // open the file
    $this->handle = fopen($this->file, 'r');

    // check the file opened successfully
    if (!$this->handle) {
      throw new Exception('Error opening file for reading');
    }
  }

  /**
   * __destruct
   * 
   * Cleans up
   *
   * @return void
   * @author Dom Hastings
   * @access public
   */
  public function __destruct() {
    // close the file resource
    fclose($this->handle);
  }

  /**
   * read
   * 
   * Reads the first available occurence of the XML element $this->options['element']
   *
   * @return string The XML string from $this->file
   * @author Dom Hastings
   * @access public
   */
  public function read() {
    // check we have an element specified
    if (!empty($this->options['element'])) {
      // trim it
      $element = trim($this->options['element']);

    } else {
      $element = '';
    }

    // initialize the buffer
    $buffer = false;

    // if the element is empty
    if (empty($element)) {
      // let the script know we're reading
      $this->reading = true;

      // read in the whole doc, cos we don't know what's wanted
      while ($this->reading) {
        $buffer .= fread($this->handle, $this->options['chunkSize']);

        $this->reading = (!feof($this->handle));
      }

      // return it all
      return $buffer;

    // we must be looking for a specific element
    } else {
      // set up the strings to find
      $open = '<'.$element.'>';
      $close = '</'.$element.'>';

      // let the script know we're reading
      $this->reading = true;

      // reset the global buffer
      $this->readBuffer = '';

      // this is used to ensure all data is read, and to make sure we don't send the start data again by mistake
      $store = false;

      // seek to the position we need in the file
      fseek($this->handle, $this->pointer);

      // start reading
      while ($this->reading && !feof($this->handle)) {
        // store the chunk in a temporary variable
        $tmp = fread($this->handle, $this->options['chunkSize']);

        // update the global buffer
        $this->readBuffer .= $tmp;

        // check for the open string
        $checkOpen = strpos($tmp, $open);

        // if it wasn't in the new buffer
        if (!$checkOpen && !($store)) {
          // check the full buffer (in case it was only half in this buffer)
          $checkOpen = strpos($this->readBuffer, $open);

          // if it was in there
          if ($checkOpen) {
            // set it to the remainder
            $checkOpen = $checkOpen % $this->options['chunkSize'];
          }
        }

        // check for the close string
        $checkClose = strpos($tmp, $close);

        // if it wasn't in the new buffer
        if (!$checkClose && ($store)) {
          // check the full buffer (in case it was only half in this buffer)
          $checkClose = strpos($this->readBuffer, $close);

          // if it was in there
          if ($checkClose) {
            // set it to the remainder plus the length of the close string itself
            $checkClose = ($checkClose + strlen($close)) % $this->options['chunkSize'];
          }

        // if it was
        } elseif ($checkClose) {
          // add the length of the close string itself
          $checkClose += strlen($close);
        }

        // if we've found the opening string and we're not already reading another element
        if ($checkOpen !== false && !($store)) {
          // if we're found the end element too
          if ($checkClose !== false) {
            // append the string only between the start and end element
            $buffer .= substr($tmp, $checkOpen, ($checkClose - $checkOpen));

            // update the pointer
            $this->pointer += $checkClose;

            // let the script know we're done
            $this->reading = false;

          } else {
            // append the data we know to be part of this element
            $buffer .= substr($tmp, $checkOpen);

            // update the pointer
            $this->pointer += $this->options['chunkSize'];

            // let the script know we're gonna be storing all the data until we find the close element
            $store = true;
          }

        // if we've found the closing element
        } elseif ($checkClose !== false) {
          // update the buffer with the data upto and including the close tag
          $buffer .= substr($tmp, 0, $checkClose);

          // update the pointer
          $this->pointer += $checkClose;

          // let the script know we're done
          $this->reading = false;

        // if we've found the closing element, but half in the previous chunk
        } elseif ($store) {
          // update the buffer
          $buffer .= $tmp;

          // and the pointer
          $this->pointer += $this->options['chunkSize'];
        }
      }
    }

    // return the element (or the whole file if we're not looking for elements)
    return $buffer;
  }
}
9
ответ дан Douglas Radburn 22 August 2018 в 14:33
поделиться
  • 1
    Благодарю. Это было действительно полезно. – Ajinkya Kulkarni 11 November 2014 в 17:58
  • 2
    Это багги, я не отлаживал его, но у меня были разные ошибки. Иногда он выводит не одну, а две строки xml. Иногда она пропускает их. – John 29 June 2018 в 17:57

Это не отличное решение, но просто нужно добавить другой вариант:

Вы можете разбивать многие большие XML-файлы на куски, особенно те, которые на самом деле являются списками похожих элементов (например, Я подозреваю, что файл, с которым вы работаете, будет).

, например, если ваш документ выглядит следующим образом:

<dmoz>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  <listing>....</listing>
  ...
</dmoz>

Вы можете прочитать его в мега или два раза за раз , искусственно оберните несколько полных тегов <listing>, загруженных в тег корневого уровня, а затем загрузите их через simplexml / domxml (я использовал domxml при использовании этого подхода).

Честно говоря, я предпочитаю этот подход если вы используете PHP & lt; 5.1.2. С 5.1.2 и выше доступен XMLReader, который, вероятно, является лучшим вариантом, но до этого вы застряли либо с вышеупомянутой стратегией chunking, либо с старой SAX / expat lib. И я не знаю обо всех вас, но я не знаю, писать / поддерживать SAX / expat парсеров.

Обратите внимание, однако, что этот подход НЕ очень практичен, когда ваш документ не состоят из множества идентичных элементов нижнего уровня (например, он отлично подходит для любого списка файлов или URL-адресов и т. д., но не имеет смысла анализировать большой HTML-документ)

4
ответ дан Frank Farmer 22 August 2018 в 14:33
поделиться

Я бы предложил использовать парсер, основанный на SAX, а не на основе DOM.

Информация об использовании SAX в PHP: http://www.brainbell.com/tutorials/php/Parsing_XML_With_SAX. HTM

4
ответ дан Tetsujin no Oni 22 August 2018 в 14:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: