2014-02-18 77 views
-1

我知道這看起來像火焰誘餌,但事實並非如此。聽我說。因爲stackexchange更喜歡問題(這主要是一個答案),讓我問「以下哪些問題?」正則表達式在Perl中解析XML或XHTML DOM

正則表達式不適合DOM解析。但是,當可用時,與複雜的DOM解析器相比,它們具有很好的通用性,易用性,並且沒有額外的學習曲線。

所以,我想我會分享一個黑客,使正則表達式適合快速和骯髒的DOM變化。它將臨時gid添加到html標籤及其關閉屬性中,然後使用正則表達式返回引用。

該代碼是仁慈短小的,並且符合perl哲學之一,即如果編程時間比運行時短,某些解決方案可能會更好。

#!/usr/bin/perl -w 
use strict; 
use warnings FATAL => qw{ uninitialized }; 

################################################################ 
sub tokenize { 
    my $GLOBAL; (defined($_[0])) and do { $GLOBAL = $_; $_ = $_[0]; }; 

    my @xmlstack; 
    my $gid=0; 

    my $addgid= sub { 
    my ($s,$e) = @_; 

    ($e =~ /\/$/) and return "<$s$e/>"; ## could add gid here, too. 

    if ($s =~ /^\/(.*)/) { 
     my $off= pop(@xmlstack); 
     ($off->[0] eq $1) or die "not a valid document at id=$gid. (wanted = $off->[0] . had = $s).\n"; 
     return "<$s gid=\"".($off->[1])."\">"; ## not legal html now, but easy to remove 
    } else { 
     push(@xmlstack, [$s, ++$gid]); 
     return "<$s gid=\"$gid\">"; 
    } 
    }; 

    my $U="#!#"; 
    (/$U/) and die "sorry, this is a hack. pick any other unique string than '$U'\n"; 
    s/<!--(.*?)-->/$U$1$U/gms; # comments can contain html tags 
    s/\<(\/?[a-zA-Z0-9]*)(.*?)\>/$addgid->($1,$2)/emsg; 
    s/$U(.*?)$U/<!--$1-->/gms; 
    (@xmlstack) and die "unfinished business: ".pop(@xmlstack)->[0]."\n"; 

    if ($GLOBAL) { my $CHANGED= $_; $_ = $GLOBAL; return $CHANGED; } else { return $_; } 
} 

sub untokenize { 
    my $GLOBAL; (defined($_[0])) and do { $GLOBAL = $_; $_ = $_[0]; }; 
    s/ gid="[0-9]+">/>/g; ## buglet: could mistakenly remove gid from inside comments. 
    if ($GLOBAL) { my $CHANGED= $_; $_ = $GLOBAL; return $CHANGED; } else { return $_; } 
} 

################################################################ 


$_ = "<html>\n<body>\n 
<p> <sup>a</sup><sub>b</sub>. </p>. 
<hr /> 
<p> hi<sup>u<sub>ud<sup>udu</sup></sub></sup> </p>. 
</body> 
</html> 
"; 


tokenize(); 

## now we can use regex using backreferences 
while (/<sup (gid="[0-9]+")>(.*)<\/sup \g1>/gms) { 
    print "Example matching of all sup's: $1 $2\n"; ## could call recursively 
} 

## another example: add a class to sup that is immediately followed by a sub 
s/\<sup (gid="[0-9]+")\>(.*)<\/sup \g1>\s*\<sub/<sup class="followed" $1>$2<\/sup $1><sup/gms; 

print untokenize($_); 

這很可能還是無知HTML併發症的整體轉換,但它可以處理大量的DOM XHTML和XML的工作,否則不適用於正則表達式解析。

+2

'/ \ <(\ /〔A-ZA-Z0-9] *)(。*?)\> /'是不正確的。使用現有工具的原因是不是因爲正則表達式是不好的,這是因爲它是很多工作的地獄來寫自己的解析器,而不是使用現有的(使用正則表達式或其他)。 – ikegami

+0

它可以在xhtml上失敗的示例?我正在查看html標籤列表,這應該在有效的html上工作......當然,它太寬鬆了。 –

+0

我明確未說明頂撞你的要求,它更容易寫自己的解析器比學習現有的一個。 – ikegami

回答

0

我發佈的解決方案很幼稚。

加:

  • 隨機的網頁,它似乎處理大約9出了互聯網上10名XHTML的網頁。它可以處理普通的堆棧xhtml文件,但可能會失敗更多不尋常的功能(如DTD等)。如果另一個程序生成了你的xhtml輸出,它可能會一直工作。

  • 這裏的學習曲線約爲1/10相比,真正的DOM解析

  • 代碼在這裏是大小的1/10左右相比,真正的DOM解析。

  • 然後可以使用熟悉的perl正則表達式知識。

  • 準備好這個工具是相當有限的。如果你超出了它的能力,無論如何,你可能不得不學習更好的DOM解析器。

減:

  • 如果需要完美的DOM解析它是完全不合適的。這段代碼是易碎的。它遵循伯克利而不是在&t方法。

  • 但是完美的DOM解析器也可能在錯誤的HTML文檔上失敗。

  • 如果你已經知道DOM解析,那麼幾乎沒有時間成本去做正確的事。使用Mojolicious或XML :: LibXML。你可以堅持更好的解決方案,然後..

給這個代碼自反-1投票忽略它有它的用途。有時候,一個普通的螺絲刀可以做一個飛利浦會更好的工作。此代碼是用於philips螺絲的普通螺絲刀。 stackoverflow是新手需要快速解決方案的網站;不只是專家。這就是爲什麼我發佈它的開始。

簡單的改進修正的讚賞,雖然這裏的目標是明確不處理XML和XHTML的所有可能的有效和無效的,理智和瘋狂,正確和不正確的排列。

/IAW