用PHP解析巨大的XML文件我正在尝试将DMOZ内容/结构XML文件解析为MySQL,但是执行此操作的所有现有脚本都很老,并且运行不正常。如何在PHP中打开一个大的(+ 1GB)XML文件进行解析?
3 回答
![?](http://img1.sycdn.imooc.com/5458463b0001358f02200220-100-100.jpg)
慕森王
TA贡献1777条经验 获得超3个赞
只有两个PHP API非常适合处理大型文件。第一个是旧的expat api,第二个是较新的XMLreader函数。这些apis读取连续流而不是将整个树加载到内存中(这就是simplexml和DOM所做的)。
例如,您可能希望查看DMOZ目录的这个部分解析器:
<?phpclass 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();
![?](http://img1.sycdn.imooc.com/54584c9c0001489602200220-100-100.jpg)
慕的地8271018
TA贡献1796条经验 获得超4个赞
我最近不得不解析一些非常大的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; }}
- 3 回答
- 0 关注
- 754 浏览
相关问题推荐
添加回答
举报
0/150
提交
取消