我有與我的劇本有每天解析超過300 000項XML文件..如何使用PHP將大型XML文件分成小型文件?
的XML與結構爲:
<root>
<item>
<proper1></proper1>
<proper2></proper2>
</item>
</root>
我需要大的XML文件分割成更小的文件所以我的PHP可以運行它們,目前它不能處理它,因爲它使用了太多的內存。 任何人都可以幫助我嗎?
我有與我的劇本有每天解析超過300 000項XML文件..如何使用PHP將大型XML文件分成小型文件?
的XML與結構爲:
<root>
<item>
<proper1></proper1>
<proper2></proper2>
</item>
</root>
我需要大的XML文件分割成更小的文件所以我的PHP可以運行它們,目前它不能處理它,因爲它使用了太多的內存。 任何人都可以幫助我嗎?
很大程度上取決於您的XML文件結構。
例如,你可以這樣做(假設結構爲您發佈,回車包括一個,否則事情變得更加複雜):
線路砍版本:超快切片大型XML如果文件格式「恰到好處」
而崩潰和燃燒,如果文件格式不只是酷似所以
$fp = fopen(XMLFILE, 'r');
$decl = fgets($fp, 1024); // Drop the XML declaration '<?xml...?>'
$root = fgets($fp, 1024); // Drop the root declaration
$n = 1;
while(!feof($fp)) {
$tag = fgets($fp, 1024);
if ('<item>' === $tag) {
isset($gp) || trigger_error('Unexpected state');
$gp = fopen("chunk{$n}.xml", 'w'); $n++;
// Write the header of the file we saved from before
fwrite($gp, $decl);
fwrite($gp, $root);
} else if ('</item>' === $tag) {
fwrite($gp, $tag);
fwrite($gp, '</root>');
fclose($gp); unset($gp);
continue;
}
if (!isset($gp)) {
if ('</root>' === $tag /* EOF */) {
break;
} else {
trigger_error('Unexpected state 2');
}
}
fwrite($gp, $tag);
}
fclose($fp);
isset($gp) || trigger_error('Unexpected state 3');
這有允許的最大好處讓你'回收'你的XML解析腳本(事實上,你可以在關閉$ gp後立即調用XML解析腳本,甚至更好,根本不寫入任何文件,而是將fwrites排入緩衝區,然後調用腳本與那個緩衝區)。
另一個優點是能夠「外包」不同的子服務器中的文件,例如是XML處理長由於DNS解析,DB調用,HTTP/SOAP調用,需要反饋等等。在這種情況下,您可以基於($ n%NUM_CLIENTS)將文件保存在不同的子目錄中,並且每個客戶端可以一次獲取一個文件,處理並刪除它並繼續。
但是,最好的處理方法是改寫腳本,以便不加載內存中的XML,但使用XML Parser支持一次解析一次。
一個折衷的辦法是使用XML解析器對XML文件進行分片,並按照「原樣」將其提供給現有腳本。
XMLPARSE版本:高效切片和切塊大型XML文件
無需擔心XML實際上是如何放在一起
的XMLPARSE職能工作,通過回調,即你給你的數據條目point(xml_parse)然後分析數據,將其分割,將各個塊路由到您定義的相應子函數。xml_parse將處理編碼和空白,從而使您免於應對相同的需求,這是上述代碼中最大的缺點之一。 xmlparse核心本身不保留數據,所以即使是千兆字節(或千兆字節)文件,我們也可以實現恆定內存實現。
因此,讓我們看看如何重寫XMLParser的代碼,並通過分割給定標籤的特定數量的重複來分塊大文件。
即輸入文件:
<root><item>(STUFF OF ITEM1)</item><item>(STUFF OF ITEM2)/item>....ITEM1234...</root>
輸出文件:
FILE1: <root><item>(1)</item><item>(2)</item>...(5)</root>
FILE2: <root><item>(6)</item><item>(7)</item>...(10)</root>
...
我們寫一個XMLPARSER將提取每個的N 「塊」 做這個(這裏N = 5)項和將其饋送到塊處理器,該處理器在接收到...時會將其包裹在標籤之間,添加XML標頭,從而生成具有與原始大文件相同語法的文件,但只有五個項目。
要保存在單獨的文件中,我們跟蹤塊號碼。
function processChunk() {
GLOBAL $CHUNKS, $PAYLOAD, $ITEMCOUNT;
if ('' == $PAYLOAD) {
return;
}
$xp = fopen($file = "output-$CHUNKS.xml", "w");
fwrite($xp, '<?xml version="1.0"?>'."\n");
fwrite($xp, "<root>");
fwrite($xp, $PAYLOAD);
fwrite($xp, "</root>");
fclose($xp);
print "Written {$file}\n";
$CHUNKS++;
$PAYLOAD = '';
$ITEMCOUNT = 0;
}
xmlparse函數需要回調函數:一個接收標記OPENING,一個標記CLOSING,一個獲取內容,另一個獲取任何內容。我們對此毫無興趣,所以我們只填補了前三名處理者。
function startElement($xml, $tag, $attrs = array()) {
GLOBAL $PAYLOAD, $CHUNKS, $ITEMCOUNT, $CHUNKON;
if (!($CHUNKS||$ITEMCOUNT)) {
if ($CHUNKON == strtolower($tag)) {
$PAYLOAD = '';
}
}
$PAYLOAD .= "<{$tag}";
foreach($attrs as $k => $v) {
$PAYLOAD .= " {$k}=\"" .addslashes($v).'"';
}
$PAYLOAD .= '>';
}
function endElement($xml, $tag) {
GLOBAL $CHUNKON, $ITEMCOUNT, $ITEMLIMIT;
dataHandler(null, "</{$tag}>");
if ($CHUNKON == strtolower($tag)) {
if (++$ITEMCOUNT >= $ITEMLIMIT) {
processChunk();
}
}
}
function dataHandler($xml, $data) {
GLOBAL $PAYLOAD;
$PAYLOAD .= $data;
}
function defaultHandler($xml, $data) {
// a.k.a. Wild Text Fallback Handler, or WTFHandler for short.
}
的createXMLParser功能是清晰
function createXMLParser($CHARSET, $bareXML = false) {
$CURRXML = xml_parser_create($CHARSET);
xml_parser_set_option($CURRXML, XML_OPTION_CASE_FOLDING, false);
xml_parser_set_option($CURRXML, XML_OPTION_TARGET_ENCODING, $CHARSET);
xml_set_element_handler($CURRXML, 'startElement', 'endElement');
xml_set_character_data_handler($CURRXML, 'dataHandler');
xml_set_default_handler($CURRXML, 'defaultHandler');
if ($bareXML) {
xml_parse($CURRXML, '<?xml version="1.0"?>', 0);
}
return $CURRXML;
}
獨立的最後供電環路,打開大文件先生,並將其發送給磨牀。
function chunkXMLBigFile($file, $tag = 'item', $howmany = 5) {
GLOBAL $CHUNKON, $CHUNKS, $ITEMLIMIT;
// Every chunk only holds $ITEMLIMIT "$CHUNKON" elements at most.
$CHUNKON = $tag;
$ITEMLIMIT = $howmany;
$xml = createXMLParser('UTF-8', false);
$fp = fopen($file, 'r');
$CHUNKS = 0;
while(!feof($fp)) {
$chunk = fgets($fp, 10240);
xml_parse($xml, $chunk, feof($fp));
}
xml_parser_free($xml);
// Now, it is possible that one last chunk is still queued for processing.
processChunk();
}
然後我們調用本機:「拆分的test.xml到項目標籤的情況下,5件」
ChunkXMLBigFile('test.xml', 'item', 5);
此實現運行作爲開頭愚蠢的分塊約五倍慢,但可以處理同一行中的標籤,甚至可以擴展以驗證XML。
分割文件當然很容易,但您需要提供更多細節,例如您希望如何分割文件。此外,如果您每次都將整個內容加載到內存中,那麼如果它是1個文件或100個文件就沒有區別,它將在完成時採用相同的內存。你需要閱讀整個過程並存儲它嗎? – Woody 2012-07-07 12:22:26
我的腳本解析文件有數據庫查詢和圖像處理,所以我認爲如果我準備xml有一些條目限制每個文件20 000 - 30 000會更好。 所以這個拆分應該用最大限制拆分條目。 – Svetoslav 2012-07-07 12:30:23