2010-08-31 91 views
3

使用Perl,我試圖解析一堆XML文件並試圖在XML中找到任何形式的URL並打印它。我的正則表達式似乎不起作用,它沒有返回任何匹配。我錯過了什麼?URL正則表達式不起作用

sub findURL{ 
local($inputLine, $outText); 
$inputLine = $_[1]; 
while (length($inputLine) > 0) 
{ 
if ($inputLine =~ /^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]\-\.])+(\.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_\.~?\-]*))*$/) 

{ 
$outText .= $&; 
$inputLine = $'; 
} 
else 
{ 
    $inputLine = ""; 
    $outText .= ""; 
} 
} 
return $outText; 
} 
+0

我不熟悉Perl但語法標記不匹配由於$ inputLine = $「; - 這可能也會影響你的問題。 – bradenkeith 2010-08-31 13:54:47

+1

@bradenkeith不,這只是SO的braindead語法高亮。 '$''是保存正則表達式匹配後的有效變量;但是,它不應該被用作預匹配,匹配和後期匹配已被替換爲不會減慢所有正則表達式代碼的捕獲。 – 2010-08-31 14:00:42

+0

@Chas。我認爲歐文斯是這方面的事情。只是覺得我只是指出了事情。感謝您的解釋。 – bradenkeith 2010-08-31 14:15:18

回答

12

使用正則表達式::常見

use Regexp::Common qw /URI/; 

while (<>) { 
    /$RE{URI}{HTTP}/  and print "Contains an HTTP URI.\n"; 
} 
+0

圖書館總是很好。 +1 – 2010-08-31 16:41:48

0

我認爲這是你認爲是一個字符類。出於某種原因編譯,但是當我隔離角色類時,調試輸出顯示出一些好奇的東西。

use strict; 
use warnings; 
use re 'debug'; 

my $re = qr/[[a-zA-Z0-9]\-\.]/; 

而且登場輸出(從use re 'debug')顯示了這個:

Compiling REx "[[a-zA-Z0-9]\-\.]" 
Final program: 
    1: ANYOF[0-9A-[a-z][] (12) 
    12: EXACT <-.]> (14) 
    14: END (0) 
anchored "-.]" at 1 (checking anchored) stclass ANYOF[0-9A-[a-z][] minlen 4 

因此它尋找文字'-.]'「錨」。因此,如果你的主機名不具有'.-]',它將永遠不會匹配。因此,就像我之前說過的,你正在關閉你的角色類,並且第一個非轉義的']'

包含破折號的最好方法是使其成爲該類的最後一個字符 - 以消除它可以指示範圍的可能性。

此外,它應該都只是一個類。你實際上關閉了第一個非轉義方括號的班級。你的角色類應爲:

[a-zA-Z0-9.-] 

而就是這樣。

此外,它可能會更好的做法是使用指定的字符類還有:

[\p{IsAlnum}.-] 
  • 我發現了另一個有趣的事情是,在']'被解釋爲字面方密切只要一人物類沒有打開。因此,你只需要逃避它,以避免結尾一個字符類,因此,包括它。相反,'[['將包含'['到角色類別中,因此沒有理由逃脫'[',除非在角色類別之外。
8

你的代碼是錯誤的七種不同的色調:

  • 你不應該使用正則表達式解析XML(見本question
  • local可能不應該用這種方式,你可能要my
  • $&$',並$`變量不應該使用(使用captures代替)
  • 你的縮進很糟糕
  • $inputLine = $_[1];抓住函數的第二個參數(第一個是什麼?)
  • 如果你要使用正則表達式,你應該使用/g regex modifer,不會推出自己的多個匹配代碼
  • 您正則表達式是捕獲的東西不應該(用(?:)進行分組,而不是()

這裏是我如何編寫你的代碼,如果我不在意,我會搶我不應該做的東西,可能會錯過我想要的東西(因爲正則表達式不夠聰明來解析XML)。請注意如何獲取評論中的URL。

#!/usr/bin/perl 

use strict; 
use warnings; 

use Regexp::Common qw/URI/; 

sub find_urls { 
    my $text = shift; 
    return $text =~ /$RE{URI}{-keep}/g; 
} 

my $xml = do { local $/; <DATA> }; 

for my $url (find_urls($xml)) { 
    print "$url\n"; 
} 

__DATA__ 
<root> 
    this is some text 
    and a URL: http://foo.com/foo.html 
    this isn't a URL http:notgrabbed.com 
    <img src="http://example.com/img.jpg" /> 
    <!-- oops, shouldn't grab this one: ftp://bar.com/donotgrab --> 
</root> 
+1

如果他只是對XML看起來像一個URL,我不認爲正則表達式是如此糟糕。用正則表達式解析XML的*結構*是一種罪過,但這似乎並不是OP想要的。 – 2010-08-31 14:10:55

+2

@Philip Potter但是你會錯過URL的東西,並找到註釋掉的東西。如果XML只是一個文本文件給他或她,那麼爲什麼提出這是XML? – 2010-08-31 14:19:51

+0

@Chas然後我們需要更多的領域特定的知識。用例將決定是否需要完整的XML解析器,或者如果這只是矯枉過正。他可能提出了XML,因爲通常任何額外的細節都是有用的? – 2010-08-31 14:23:59

0

有幾條評論與你的問題沒有直接關係,而是與你的代碼有關。

  1. 我不明白你爲什麼在你提供的上下文中使用local。我的直覺是你應該使用my而不是local
  2. $inputLine = $_[1]其實意味着您想要將您傳遞給參數URL的第二個參數指定爲$inputline。這是你真正想要的嗎?

關於你的正則表達式:

不要窩字符類:如[[a-zA-Z0-9]\-\.]應該[-a-zA-Z0-9.](你需要把被替換 - 一是爲了避免混淆與間隔分離器,並執行不需要在角色類中轉義)。

替換你的正則表達式/^(((http|https|ftp):\/\/)?([-a-zA-Z0-9.])+(\.)([a-zA-Z0-9]){2,4}([-a-zA-Z0-9+=%&_.~?\/]*))*$/適合我。

RFC3986當然,附錄B提供了更好的正則表達式。

2

使用可從CP​​AN獲得的URI::FindURI::Find::Schemeless模塊。例如

#! /usr/bin/perl 

use warnings; 
use strict; 

use URI::Find; 
use URI::Find::Schemeless; 

my $xml = join "" => <DATA>; 
URI::Find   ->new(sub { print "$_[1]\n" })->find(\$xml); 
URI::Find::Schemeless->new(sub { print "$_[1]\n" })->find(\$xml); 

__DATA__ 
<foo> 
    <bar>http://stackoverflow.com/</bar> 
    <baz>www.perl.com</baz> 
</foo> 

輸出:

http://stackoverflow.com/ 
www.perl.com