重いXMLメソッド

画像



職場で、彼らは私に、大量のXMLファイル(100Mb以上)をよりよく解析する手段を使用して調査を行うように頼みました。 私は、コミュニティに結果に精通するよう勧めます。



XMLを使用する基本的な方法を検討してください。



1.シンプルなXML( ドキュメント

2. DOM( ドキュメント

3. xml_parser(SAX)( ドキュメント

4. XMLReader( ドキュメント



シンプルなxml



短所 :非常にゆっくり動作し、ファイル全体をメモリに収集します。ツリーは別の配列にコンパイルされます。

長所 :使いやすさ、すぐに使える(ほぼすべてのサーバーに含まれているlibxmlライブラリが必要)



単純なXMLの例
$xml = simplexml_load_file("price.xml"); echo "<table border='1'>\n"; foreach ($xml->xpath('/DocumentElement/price') as $producs) { ?> <tr> <td><?php echo $producs->name; ?></td> <td><?php echo $producs->company; ?></td> <td><?php echo $producs->city; ?></td> <td><?php echo $producs->amount ?></td> </tr> <? } echo "</table>\n";
      
      







ドム



短所 :以前のすべての例と同様に、ファイル全体をメモリに収集するため、動作が非常に遅くなります。

長所 :出力は使い慣れたDOMであり、操作が非常に簡単です。



DOMの例
 $doc = new DOMDocument(); $doc->load( 'books.xml' ); $books = $doc->getElementsByTagName( "book" ); foreach( $books as $book ) { $authors = $book->getElementsByTagName( "author" ); $author = $authors->item(0)->nodeValue; $publishers = $book->getElementsByTagName( "publisher" ); $publisher = $publishers->item(0)->nodeValue; $titles = $book->getElementsByTagName( "title" ); $title = $titles->item(0)->nodeValue; echo "$title - $author - $publisher\n";
      
      







xml_parserおよびXMLReader。



前の2つは、ファイル全体を操作するため、適切ではありません。 それぞれ20〜30 Mbのファイルがあり、それらを操作している間、一部のブロックは100> Mbのチェーン(配列)を形成します。



どちらの方法も、ファイルを1行ずつ読み取ることで機能します。これはタスクに最適です。



xml_parserとXMLReaderの違いは、最初のケースでは、タグの開始と終了に応答する独自の関数を記述する必要があることです。



簡単に言えば、xml_parserは2つのトリガーを介して動作します-タグが開いている、タグが閉じている。 彼はそこで何が起こっているのか、どのデータが使われているのかなど気にしません。 作業のために、処理機能を示す2つのトリガーを設定します。



Xml_parserの例
 class Simple_Parser { var $parser; var $error_code; var $error_string; var $current_line; var $current_column; var $data = array(); var $datas = array(); function parse($data) { $this->parser = xml_parser_create('UTF-8'); xml_set_object($this->parser, $this); xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1); xml_set_element_handler($this->parser, 'tag_open', 'tag_close'); xml_set_character_data_handler($this->parser, 'cdata'); if (!xml_parse($this->parser, $data)) { $this->data = array(); $this->error_code = xml_get_error_code($this->parser); $this->error_string = xml_error_string($this->error_code); $this->current_line = xml_get_current_line_number($this->parser); $this->current_column = xml_get_current_column_number($this->parser); } else { $this->data = $this->data['child']; } xml_parser_free($this->parser); } function tag_open($parser, $tag, $attribs) { $this->data['child'][$tag][] = array('data' => '', 'attribs' => $attribs, 'child' => array()); $this->datas[] =& $this->data; $this->data =& $this->data['child'][$tag][count($this->data['child'][$tag])-1]; } function cdata($parser, $cdata) { $this->data['data'] .= $cdata; } function tag_close($parser, $tag) { $this->data =& $this->datas[count($this->datas)-1]; array_pop($this->datas); } } $xml_parser = new Simple_Parser; $xml_parser->parse('<foo><bar>test</bar></foo>');
      
      







XMLReaderはよりシンプルです。 まず、これはクラスです。 すべてのトリガーは既に定数で定義されており(合計で17個あります)、読み取りはread()関数によって実行され、指定されたトリガーに適した最初のオカレンスを読み取ります。 次に、データ型(alaトリガー)が入力されるオブジェクト、タグの名前、その値を取得します。 XMLReaderは、タグ属性ともうまく機能します。



XMLReaderの例
 <?php <?php Class StoreXMLReader { private $reader; private $tag; // if $ignoreDepth == 1 then will parse just first level, else parse 2th level too private function parseBlock($name, $ignoreDepth = 1) { if ($this->reader->name == $name && $this->reader->nodeType == XMLReader::ELEMENT) { $result = array(); while (!($this->reader->name == $name && $this->reader->nodeType == XMLReader::END_ELEMENT)) { //echo $this->reader->name. ' - '.$this->reader->nodeType." - ".$this->reader->depth."\n"; switch ($this->reader->nodeType) { case 1: if ($this->reader->depth > 3 && !$ignoreDepth) { $result[$nodeName] = (isset($result[$nodeName]) ? $result[$nodeName] : array()); while (!($this->reader->name == $nodeName && $this->reader->nodeType == XMLReader::END_ELEMENT)) { $resultSubBlock = $this->parseBlock($this->reader->name, 1); if (!empty($resultSubBlock)) $result[$nodeName][] = $resultSubBlock; unset($resultSubBlock); $this->reader->read(); } } $nodeName = $this->reader->name; if ($this->reader->hasAttributes) { $attributeCount = $this->reader->attributeCount; for ($i = 0; $i < $attributeCount; $i++) { $this->reader->moveToAttributeNo($i); $result['attr'][$this->reader->name] = $this->reader->value; } $this->reader->moveToElement(); } break; case 3: case 4: $result[$nodeName] = $this->reader->value; $this->reader->read(); break; } $this->reader->read(); } return $result; } } public function parse($filename) { if (!$filename) return array(); $this->reader = new XMLReader(); $this->reader->open($filename); // begin read XML while ($this->reader->read()) { if ($this->reader->name == 'store_categories') { // while not found end tag read blocks while (!($this->reader->name == 'store_categories' && $this->reader->nodeType == XMLReader::END_ELEMENT)) { $store_category = $this->parseBlock('store_category'); /* Do some code */ $this->reader->read(); } $this->reader->read(); } } // while } // func } $xmlr = new StoreXMLReader(); $r = $xmlr->parse('example.xml');
      
      







性能試験



ジェネレーターコードexample.xml
 <?php $xmlWriter = new XMLWriter(); $xmlWriter->openMemory(); $xmlWriter->startDocument('1.0', 'UTF-8'); $xmlWriter->startElement('shop'); for ($i=0; $i<=1000000; ++$i) { $productId = uniqid(); $xmlWriter->startElement('product'); $xmlWriter->writeElement('id', $productId); $xmlWriter->writeElement('name', 'Some product name. ID:' . $productId); $xmlWriter->endElement(); // Flush XML in memory to file every 1000 iterations if (0 == $i%1000) { file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND); } } $xmlWriter->endElement(); // Final flush to make sure we haven't missed anything file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND);
      
      







テスト結果(無差別に読む)



テスト環境の特性
Ubuntu 16.04.1 LTS

PHP 7.0.15

Intel®Core(TM)i5-3550 CPU @ 3.30GHz、16 Gb RAM、256 SSD



方法 リードタイム(19 Mb) リードタイム(190 Mb)
シンプルなxml 0.46秒 4.56秒
ドム 0.52秒 4.09秒
xml_parse 0.22秒 2.25秒
XMLリーダー 0.26秒 2.18秒


PSヒントとコメント喜んで聞きます。 あまり蹴らないようにお願いします



All Articles