2011-05-09 42 views
0

我有以下文字轉換遞歸標籤到數組正則表達式

hello <?tag?> world <?tag2?> xx <?/tag2?> hello <?/tag?> world

,我需要它轉換成

陣列( 「你好」, 陣列( 「世界」, array( 'xx' ), 'hello' ), 'world' );

標籤是字母數字,只要它們用匹配標籤關閉,或<?/?>。具有相同名稱的標籤可能重複,但不會在每個其他內部。

我的問題是哪個是最具CPU效率的方法?

  • 使用遞歸的preg_replace與回調
  • 使用preg_match_all與PREG_OFFSET_CAPTURE
  • 使用使preg_split到flattern所有標籤(PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE),成線性陣列然後穿行和組標籤。

如果你還可以提供的表達,我會很高興。

+0

你所描述語言,是不是定期。這意味着正則表達式不是一個合適的解決方案 – webbiedave 2011-05-09 21:43:40

+0

添加了不能嵌套同名標籤的規則。 – romaninsh 2011-05-09 21:47:18

+1

@webbiedave:*理論*正則表達式不合適,但現在大多數所謂的正則表達式實現也允許您匹配非常規語言。 – jwodder 2011-05-09 21:48:24

回答

1

事實並非如此簡單,但希望這對其他人有所幫助。最大的難題是從preg_replace的回調函數返回非字符串。

謝謝所有誰試圖幫助!

class Parser { 
    public $ret=array(); 
    function loadTemplateFromString($str){ 
     $this->parsed_template=$this->tags=array(); 
     if(!$str){ 
      return; 
     } 
     var_dump($str); 

     /* First expand self-closing tags <?$tag?> -> <?tag?><?/tag?> */ 
     $str=preg_replace('/<\?\$([\w]+)\?>/','<?\1?><?/\1?>',$str); 

     /* Next fix short ending tag <?tag?> <?/?> -> <?tag?> <?/?> */ 
     $x=preg_replace_callback('/.*?<\?\/\?>/',function($x){ 
       return preg_replace('/(.*<\?([^\/][\w]+)\?>)(.*?)(<\?\/?\?>)/', 
        '\1\3<?/\2?>',$x[0]); 
       },$str); 

     /* Finally recursively build tag structure */ 
     $this->recursiveReplace($x); 
    } 
    function recursiveReplace($x){ 

     if(is_array($x)){ 
      // Called recursively 
      $tmp2=$this->ret;$this->ret=array(); 
     }else{ 
      $x=array(4=>$x); 
      $tmp2=null; 
     } 
     $y=preg_replace_callback('/(.*?)(<\?([^\/$][\w]+)\?>)(.*?)(<\?\/(\3)?\?>)(.*?)/', 
      array($this,'recursiveReplace'),$x[4]); 
     $this->ret[]=$y; 
     if($tmp2===null)return; 
     $tmp=$this->ret; 
     $this->ret=$tmp2; 

     $this->ret[]=$x[1]; 
     $this->ret[]=$tmp; 
     return ''; 
    } 
} 
$p=new Parser(); 
$p->loadTemplateFromString('bla <?name?> name <?/name?> bla bla <?$surname?> bla '. 
    '<?middle?> mm <?/?> blah <?outer?> you <?inner?> are <?/?> inside <?/outer?>'. 
    ' bobobo'); 
var_dump($p->ret); 

此輸出:

array 
    0 => string 'bla ' (length=4) 
    1 => 
    array 
     0 => string ' name ' (length=6) 
    2 => string ' bla bla ' (length=9) 
    3 => 
    array 
     0 => string '' (length=0) 
    4 => string ' bla ' (length=5) 
    5 => 
    array 
     0 => string ' mm ' (length=4) 
    6 => string ' blah ' (length=6) 
    7 => 
    array 
     0 => string ' you ' (length=5) 
     1 => 
     array 
      0 => string ' are ' (length=5) 
     2 => string ' inside ' (length=8) 
    8 => string ' bobobo' (length=7) 
+1

幹得好!在'recursiveReplace(...)'我認爲你可以省略最後一個組('(。*?)'),因爲我認爲它永遠不會被使用,因爲它在模式的末尾,但是有一個懶惰的量詞。 – mousio 2011-05-10 00:15:16

+0

啊,好點:)我添加它作爲實驗,永遠不會刪除。 – romaninsh 2011-05-10 01:15:36

+0

使用這種方法,我依靠子字符串匹配比以前的實現提高了3倍的性能。 – romaninsh 2011-05-10 01:16:22

0

<?tag?>改爲<elem>並將其解析爲XML?

當你得到一個看起來像你提到的結果的原始結構後,你可以/根據你的元素結構驗證它(也就是說,確保項目在數值上相互等)。

只添加的文檔元素和你設置這個樣式表:


編輯:的事實,這些標籤被混合使用HTML來了,我想我會改變我的策略之後。描述前請先查看以下代碼:

$data = '<b>H</b>ello <?tag?> <b>W</b>orld <?/tag?>'; 

$conv1 = array(
// original => entity 
    '<?tag' => '%START-BEGIN%', 
    '<?/tag' => '%START-END%' 
    '?>'  => '%END-END%' 
); 

$conv2 = array(
// entity   => xml 
    '%START-BEGIN%' => '<element', 
    '%START-END%' => '</element' 
    '%END-END%'  => '>' 
); 

$data = str_replace(array_keys($conv1), array_values($conv1), data); 

$data = htmlentities($data, ENT_QUOTES); // encode HTML characters 

$data = str_replace(array_values($conv2), array_keys($conv2), data); 

$xml = '<?xml version="1.0" encoding="UTF-8"?>'.$data; 

// You must apply the following function to each output text 
// html_entity_decode($data,ENT_QUOTES); 
+0

剩餘文本是HTML,因此會混淆。 – romaninsh 2011-05-09 22:56:37

+0

啊,好吧,我試過**:)**。讓我重新考慮一下。 – Christian 2011-05-09 22:57:36

+0

感謝@mousio的建議。 – Christian 2011-05-09 22:59:41