利用Perl解析XML檔案

miguelmin發表於2009-01-17
當在Perl中使用XML時,你會有將近五百個CPAN模組可以選擇,每一個都支援整合Web服務的不同方面。此外,Perl的核心庫包括多個支援XML的模組。這篇文章就關注於一個最早期且涉及最頻繁的核心模組:XML::Parser.
XML::解析器系列
最初的Perl解析器XML::Parser::Expat由Larry Wall在幾年前編寫並由Clark Cooper保持延續。模組作為Expat XML parser的介面由James Clark用C語言編寫且已經被一些指令碼語言所採用。

Expat是一個以事件為基礎的解析器,意味著特定條件觸發處理功能。例如,一個開始或結尾標記將觸發適當的使用者定義子程式。XML::Parser模組在Expat功能的基礎上為普通應用所建構。[@more@]注意Expat在解析之前不會使XML生效且在遇到錯誤出現時會失效。這些限制會使XML::Parser模組速度非常之快。
XML::Parser簡介
任何人都可以在Perl中編寫一個XML解析器。畢竟你只是處理具有已知格式的文字。但由於XML::Parser模組是用C編寫的,他比任何你可以實現的純Perl應用要更有效的多。而且既然他已經被編寫出來,你就可以把時間花在其他更有用的事情上。

XML::Parser的Expat的功能允許你定義所使用的解析器的樣式。最普遍使用的樣式是Tree和Stream,Tree處理XML的輸入,建立含有檔案中的元素和資料的成套陣列,這樣你就可以按照你的意願控制這個結構。Stream將分析行為劃分層級,在一個事件的開始做處理,要使用Stream,你必須在你對模組做例示並將其與描述事件出現時如何處理的子程式相關聯時定義處理器。

其它樣式還包括:Sub,允許你特定地對一種XML標記定義其功能。Debug,將檔案顯示未標準輸出。Object,與Tree相似但是返回物件。你也可以透過在XML::Parser類中定義一個子類來設定一個客制樣式。

一個例子
這個例子中,我將使用XML::Parser類來建立一個Stream解析。我將帶出一個將XML檔案解析為標準輸出的簡單指令碼程式,你可以在表A中看到指令碼程式(xmlparse.pl),在表B中看到XML檔案(data.xml)。這裡,由於這是一個命令列指令碼,我選擇不解析URL元素。要執行這個指令碼程式,在命令提示下,鍵入:

perl xmlparse.pl data.xml

指令碼先參照適當的模組:

use XML::Parser;
 

接著,從命令提示輸入抓取檔案:

my $xmlfile = shift;
die "Cannot find file "$xmlfile""
unless -f $xmlfile;

指令碼設定一些初始變數:

$count = 0;
$tag = "";

然後是建立解析器例項:

my $parser = new XML::Parser;

現在我們定義事件處理器。我設定了開始標記,結束標記,特性資料的處理器。而僅僅因為是例子,我還加入了一個預設處理器,它將對全部進行解析而不被其他事件處理器的定義所明顯覆蓋。如果你計劃丟棄額外資料,預設處理器將自動執行而不需要定義。

$parser->setHandlers( Start => &startElement,
End => &endElement,
Char => &characterData,
Default => &default);

指令碼的主要部分透過指示解析器例項而變得緊湊來透過XML資料檔案:

$parser->parsefile($xmlfile);

剩下的就是定義在每個事件情況下如何進行處理。

當指令碼程式遇到一個開始標記時將執行這個子程式,這在上面的setHandlers方式中進行了定義。我選擇跳過去並顯示每個元素中我感興趣的文字。

接下來我定義的變數自動地被XML::Parser模組所傳遞。對於開始標記處理器,這些變數代表解析器例項,標記名和標記可能會有的屬性陣列。如果標記沒有屬性,則一個空陣列將被傳遞至子程式。

sub startElement {
my( $parseinst, $element, %attrs ) = @_;
SWITCH: {
if ($element eq "article") {
$count++;
$tag = "article";
print "Article $count:n";
last SWITCH;
}
if ($element eq "title") {
print "Title: ";
$tag = "title";
last SWITCH;
}
if ($element eq "summary") {
print "Summary: ";
$tag = "summary";
last SWITCH;
}
}
}

在XML資料檔案中遇到一個結束標記,endElement子程式就會被呼叫。這裡,我要提供一些線中斷。在這裡被XML::Parser所傳遞的變數是解析器例項和標記名稱。

sub endElement {
my( $parseinst, $element ) = @_;
if ($element eq "article") {
print "nn";
} elsif ($element eq "title") {
print "n";
}
}

由於我們處在命令列,我使用特性資料處理器來去掉可能已經包括在XML資料檔案中的任一行和標籤格式並選擇顯示內容(如果他來自一個標題或摘要標記)。

sub characterData {
my( $parseinst, $data ) = @_;
if (($tag eq "title") || ($tag eq "summary")) {
$data =~ s/n|t//g;
print "$data";
}
}

最後,我定義了一個子程式來處理所有可能遇到的其它型別的元素。其中包括特性編碼定義,檔案型別定義和評註。所有不被開始標記,結束標記和特性資料事件處理器所明確覆蓋的都包括在這裡面。

sub default {
my( $parseinst, $data ) = @_;
# you could do something here
}


小結
你對XML::Parser的Expat功能熟悉之後,你可以將其作為接觸幾百個可用的CPAN XML模組的跳板。我們這裡的Stream樣式是唯一一個可用的XML::Parser模組中的解析型別。你可能會發現其他的會更適合你的工作任務。Perl幾乎從第一個工程方案推出時就具有XML功能,不論你的需要如何,他都是一個很好的選擇。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16723161/viewspace-1016241/,如需轉載,請註明出處,否則將追究法律責任。

相關文章