2010-08-31 115 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