我讀過這篇着名的文章。我看到了這些嘗試,無論是有限的成功還是失敗。哦,在這裏和其他地方的火焰戰爭。用正則表達式解析XML/XHTML數據
但它可以做到。
雖然我知道實際的參數(讀事實)是正則表達式不適合解析結構化數據樹,但由於它們無法監視和更改狀態,所以我覺得有些人會盲目放棄這種可能性。應用邏輯必須保持狀態,但正如工作示例所示,它可以完成。
相關片段如下:
const PARSE_MODE_NEXT = 0;
const PARSE_MODE_ELEMENT = 1;
const PARSE_MODE_ENTITY = 3;
const PARSE_MODE_COMMENT = 4;
const PARSE_MODE_CDATA = 5;
const PARSE_MODE_PROC = 6;
protected $_parseModes = array(
self::PARSE_MODE_NEXT => '% < (?: (?: (?<entity>!) (?: (?<comment>--) | (?<cdata>\[CDATA\[))) | (?<proc>\?))? %six',
self::PARSE_MODE_ELEMENT => '% (?<close>/)? (?<element> .*?) (?<empty> /)? > (?<text> [^<]*) %six',
self::PARSE_MODE_ENTITY => '% (?<entity> .*?) > (?<text> [^<]*) %six',
self::PARSE_MODE_COMMENT => '% (?<comment> .*?) --> (?<text> [^<]*) %six',
self::PARSE_MODE_CDATA => '% (?<cdata> .*?) \]\]> (?<text> [^<]*) %six',
self::PARSE_MODE_PROC => '% (?<proc> .*?) \?> (?<text> [^<]*) %six',
);
public function load($string){
$parseMode = self::PARSE_MODE_NEXT;
$parseOffset = 0;
$context = $this;
while(preg_match($this->_parseModes[$parseMode], $string, $match, PREG_OFFSET_CAPTURE, $parseOffset)){
if($parseMode == self::PARSE_MODE_NEXT){
switch(true){
case (!($match['entity'][0] || $match['comment'][0] || $match['cdata'][0] || $match['proc'][0])):
$parseMode = self::PARSE_MODE_ELEMENT;
break;
case ($match['proc'][0]):
$parseMode = self::PARSE_MODE_PROC;
break;
case ($match['cdata'][0]):
$parseMode = self::PARSE_MODE_CDATA;
break;
case ($match['comment'][0]):
$parseMode = self::PARSE_MODE_COMMENT;
break;
case ($match['entity'][0]):
$parseMode = self::PARSE_MODE_ENTITY;
break;
}
}else{
switch($parseMode){
case (self::PARSE_MODE_ELEMENT):
switch(true){
case (!($match['close'][0] || $match['empty'][0])):
$context = $context->addChild(new ZuqMLElement($match['element'][0]));
break;
case ($match['empty'][0]):
$context->addChild(new ZuqMLElement($match['element'][0]));
break;
case ($match['close'][0]):
$context = $context->_parent;
break;
}
break;
case (self::PARSE_MODE_ENTITY):
$context->addChild(new ZuqMLEntity($match['entity'][0]));
break;
case (self::PARSE_MODE_COMMENT):
$context->addChild(new ZuqMLComment($match['comment'][0]));
break;
case (self::PARSE_MODE_CDATA):
$context->addChild(new ZuqMLCharacterData($match['cdata'][0]));
break;
case (self::PARSE_MODE_PROC):
$context->addChild(new ZuqMLProcessingInstruction($match['proc'][0]));
break;
}
$parseMode = self::PARSE_MODE_NEXT;
}
if(trim($match['text'][0])){
$context->addChild(new ZuqMLText($match['text'][0]));
}
$parseOffset = $match[0][1] + strlen($match[0][0]);
}
}
是否完整?沒有。
它是否牢不可破?當然不是。
它快嗎?沒有進行基準測試,但我無法想象它的速度如同DOM
一樣快。
它支持XPath/XQuery嗎?顯然不是。
它驗證或執行任何其他輔助任務嗎?當然沒有。
它會取代DOM嗎?地獄號碼。
但是,它會解析這個嗎?
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY name="value">
<root>
<node>
<node />
Foo
<node name="value">
<node>Bar</node>
</node>
<!-- Comment -->
</node>
<node>
<[CDATA[ Character Data ]]>
</node>
</root>
是。是的,它會。
儘管我會歡迎該主題成爲社區Wiki,只要它滿足要求,我會將此語句轉換爲一個問題。
專注於正則表達式,任何人都可以預見這種情況下,如果使用格式良好的標記,這種情況會失敗嗎?我想我已經涵蓋了所有的基地。
我不打算「攪拌鍋」,但我希望從硬幣的兩側都有一些見解。
請注意,寫這個的目的是SimpleXML
太簡單了,DOM
對於我的一個應用程序來說太嚴格了。
當你提出一些不容爭辯的點,主要的原因已經寫了這個有:`SimpleXML`太簡單了,我需要的東西,提供有限的支持; `DOM`太嚴格了,導致比解決問題更多的問題;我需要一些適合遷移的東西,無論環境限制如何,都可以輕鬆納入任何項目。爲了達到目的,我不關心驗證,只關心解析和重構結構化文檔的能力。 *但是*,速度是一個問題,而在比`超過30倍慢(100,000測試迭代)DOM`,失敗,且無法使用。 :( – Dan 2011-01-26 20:13:22