URL Regex无效

时间:2010-08-31 13:51:23

标签: regex perl

使用Perl,我试图解析一堆XML文件,并尝试在XML中查找任何形式的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;
}

5 个答案:

答案 0 :(得分:12)

使用Regexp :: Common

use Regexp::Common qw /URI/;

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

答案 1 :(得分: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>

答案 2 :(得分:2)

使用CPAN提供的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

答案 3 :(得分: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}.-]
  • 我发现的另一个有趣的事情是']'被解释为文字方形 - 关闭字符类未打开的地方。因此,您只需要将其转义以避免结束一个字符类,从而包含它。相反,'[[''['包含在角色类中,因此没有理由逃避'[',除非外的字符类。

答案 4 :(得分:0)

一些评论与您的​​问题没有直接关系,而是与您的代码直接相关。

  1. 我不明白为什么你在你提供的上下文中使用local。我的直觉是你应该使用my而不是local
  2. $inputLine = $_[1]实际上意味着您要将传递给findURL的第二个参数分配给$inputline。这真的是你的意图吗?
  3. 关于你的正则表达式:

    不要嵌套字符类:例如[[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当然提供了更好的正则表达式。