2008-11-28 87 views
2

我在下面列出了一個文本列表,它來自一個名爲EVE Online的流行在線遊戲,當你殺死一個人在遊戲中時,它基本上會郵寄給你。我正在構建一個工具來解析這些使用PHP來提取所有相關信息。我將需要顯示所有信息,並且正在撰寫類以很好地將其分解爲相關的封裝數據。以PHP解析動態文本列表的最佳方式

2008.06.19 20:53:00 

Victim: Massi 
Corp: Cygnus Alpha Syndicate 
Alliance: NONE 
Faction: NONE 
Destroyed: Raven 
System: Jan 
Security: 0.4 
Damage Taken: 48436 

Involved parties: 

Name: Kale Kold 
Security: -10.0 
Corp: Vicious Little Killers 
Alliance: NONE 
Faction: NONE 
Ship: Drake 
Weapon: Hobgoblin II 
Damage Done: 22093 

Name: Harulth (laid the final blow) 
Security: -10.0 
Corp: Vicious Little Killers 
Alliance: NONE 
Faction: NONE 
Ship: Drake 
Weapon: Caldari Navy Scourge Heavy Missile 
Damage Done: 16687 

Name: Gistatis Tribuni/Angel Cartel 
Damage Done: 9656 

Destroyed items: 

Capacitor Power Relay II, Qty: 2 
Paradise Cruise Missile, Qty: 23 
Cataclysm Cruise Missile, Qty: 12 
Small Tractor Beam I 
Alloyed Tritanium Bar, Qty: 2 (Cargo) 
Paradise Cruise Missile, Qty: 1874 (Cargo) 
Contaminated Nanite Compound (Cargo) 
Capacitor Control Circuit I, Qty: 3 
Ballistic Deflection Field I 
'Malkuth' Cruise Launcher I, Qty: 3 
Angel Electrum Tag, Qty: 2 (Cargo) 

Dropped items: 

Ballistic Control System I 
Shield Boost Amplifier I, Qty: 2 
Charred Micro Circuit, Qty: 4 (Cargo) 
Capacitor Power Relay II, Qty: 2 
Paradise Cruise Missile, Qty: 10 
Cataclysm Cruise Missile, Qty: 21 
X-Large Shield Booster II 
Cataclysm Cruise Missile, Qty: 3220 (Cargo) 
Fried Interface Circuit (Cargo) 
F-S15 Braced Deflection Shield Matrix, Qty: 2 
Salvager I 
'Arbalest' Cruise Launcher I 
'Malkuth' Cruise Launcher I, Qty: 2 

我正在考慮使用正則表達式來解析數據,但你會怎麼做呢?你會將郵件摺疊成一行字符串還是解析數組中的每一行?麻煩的是有一些異常可以解釋。

首先,「涉及到的各方:」部分是動態的,可以包含大量具有類似結構的人員,但如果計算機控制的敵人也會對受害者進行射擊,則會縮短爲「姓名'和'Damage Done'字段,如上所示(Gistatis Tribuni/Angel Cartel)。

其次,'毀壞'和'丟棄'物品是動態的,並且每封郵件的長度都不相同,我還需要獲得數量和其他貨物的數量。

一種方法的想法是受歡迎的。

回答

3

如果你想要靈活的東西,使用狀態機的方法。

如果你想快速和骯髒的東西,使用正則表達式。

對於第一種解決方案,您可以使用專門用於parsin的庫,因爲它不是一項簡單的任務。但由於它是相當簡單的格式,你可以破解一個天真的解析器,例如:

<?php 

class Parser 
{ 
    /* Enclosing the parser in a class is not mandatory but it' clean */ 

    function Parser() 
    { 

     /* data holder */ 
     $this->date = ''; 
     $this->parties = array(); 
     $this->victim = array(); 
     $this->items = array("Destroyed" => array(), 
              "Dropped" => array()); 

     /* Map you states on actions. Sub states can be necessary (and sub parsers too :-) */     
     $this->states = array('Victim' => 'victim_parsing', 
              'Involved' => 'parties_parsing' , 
              'items:' => "item_parsing"); 


     $this->state = 'start';      
     $this->item_parsing_state = 'Destroyed';  
     $this->partie_parsing_state = '';   
     $this->parse_tools = array('start' => 'start_parsing', 
              'parties_parsing' =>'parties_parsing', 
              'item_parsing' => 'item_parsing', 
              'victim_parsing' => 'victim_parsing'); 


    } 

    /* the magic job is done here */ 

    function checkLine($line) 
    { 
     foreach ($this->states as $keyword => $state) 
      if (strpos($line, $keyword) !== False) 
        $this->state = $this->states[$keyword]; 

     return trim($line); 
    } 

    function parse($file) 
    { 
     $this->file = new SplFileObject($file); 
     foreach ($this->file as $line) 
      if ($line = $this->checkLine($line)) 
       $this->{$this->parse_tools[$this->state]}($line); 
    } 


    /* then here you can define as much as parsing rules as you want */ 

    function victim_parsing($line) 
    { 
     $victim_caract = explode(': ', $line); 
     $this->victim[$victim_caract[0]] = $victim_caract[1]; 
    } 

    function start_parsing($line) 
    { 
     $this->date = $line; 
    } 

    function item_parsing($line) 
    { 
     if (strpos($line, 'items:') !== False) 
     { 
      $item_state = explode(' ', $line); 
      $this->item_parsing_state = $item_state[0]; 
     } 
      else 
     { 
       $item_caract = explode(', Qty: ', $line); 
       $this->items[$this->item_parsing_state][$item_caract[0]] = array(); 
       $item_infos = explode(' ', $item_caract[1]); 
       $this->items[$this->item_parsing_state][$item_caract[0]] ['qty'] = empty($item_infos[0]) ? 1 : $item_infos[0]; 
       $this->items[$this->item_parsing_state][$item_caract[0]] ['cargo'] = !empty($item_infos[1]) ? "True": "False"; 
       if (empty($this->items[$this->item_parsing_state][$item_caract[0]] ['qty'])) 
       print $line; 
     } 
    } 

    function parties_parsing($line) 
    {   

     $partie_caract = explode(': ', $line); 

     if ($partie_caract[0] == "Name") 
     { 
      $this->partie_parsing_state = $partie_caract[1]; 
      $this->parties[ $this->partie_parsing_state ] = array(); 
     } 
     else 
      $this->parties[ $this->partie_parsing_state ][$partie_caract[0]] = $partie_caract[1]; 

    } 

} 

/* a little test */ 

$parser = new Parser(); 
$parser->parse('test.txt'); 

echo "======== Fight report - ".$parser->date." ==========\n\n"; 
echo "Victim :\n\n"; 
print_r($parser->victim); 
echo "Parties :\n\n"; 
print_r($parser->parties); 
echo "Items: \n\n"; 
print_r($parser->items); 

?> 

我們能做到這一點,因爲在這裏,可靠性和PERF是不是一個問題:-)

快樂遊戲!

12

我可能會採用狀態機的方法,按順序讀取每一行並根據當前狀態進行處理。

有些行像「Dropped items:」更改狀態,導致您將下列行解釋爲項目。在「閱讀有關各方」的狀態中,你會將每一行添加到關於該人的一系列數據中,而當你閱讀一個空白行時,你知道你有一個完整的記錄。

這裏有一個粗略的FSM我的GraphViz

State machine http://i34.tinypic.com/4zvtc5.png

敲起來有些邊緣觸發動作在你的代碼,就像讀空行。

+0

我無法與圖片競爭,因爲+1也打破了div :) – Owen 2008-11-28 09:51:17

+0

Definitly最專業的答案。但我不確定這會對他有多大幫助。它適用於EVE在線,不適用於ADA輸出解析器... – 2008-11-28 12:59:15