Perl正则表达式替换URL

时间:2013-03-05 20:33:43

标签: regex perl url

我正在尝试使用复杂的正则表达式来匹配文本正文中的URL。目的是划分文本中的URL。

我想做类似下面的事情

perl -pe 's/regex/left $1 right/g;' inputfile

将用词leftright所包围的匹配值替换所有出现的正则表达式。这只是一个简单的例子来说明这一点 - 真实场景的载荷为{{1我希望为这个特定的匹配目的添加另一个表达式。

正则表达式与URL匹配。我意识到匹配的URL是非常困难的,并且可能无法识别所有可能性,但合理的近似值会很好。我在http://daringfireball.net/2010/07/improved_regex_for_matching_urls找到了一个这样的近似值。

但是,我不能将正则表达式用于像上面这样的perl构造中。我尝试使用与-e不同的分隔符,例如/,但没有成功。

3 个答案:

答案 0 :(得分:5)

RFC 2396的附录B给出了解析URI的正则表达式。

  

B中。使用正则表达式解析URI引用

     

如第4.3节所述,通用URI语法不足以消除某些形式的URI的组件歧义。由于该部分中描述的“贪婪算法”与POSIX正则表达式使用的消歧方法相同,因此使用正则表达式来解析URI引用的潜在四个组件和片段标识符是很自然和平常的。

     

以下行是用于将URI引用分解为其组件的正则表达式。

^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
 12            3  4          5       6  7        8 9
     

上面第二行中的数字只是为了提高可读性;它们表示每个子表达式的参考点(,每个配对括号)。我们将子表达式匹配的值 n 称为$<n>。例如,将上述表达式与

匹配
http://www.ics.uci.edu/pub/ietf/uri/#Related
     

导致以下子表达式匹配:

$1 = http:
$2 = http
$3 = //www.ics.uci.edu
$4 = www.ics.uci.edu
$5 = /pub/ietf/uri/
$6 = <undefined>
$7 = <undefined>
$8 = #Related
$9 = Related
     

其中<undefined>表示该组件不存在,如上例中的查询组件的情况。因此,我们可以确定四个组件和片段的值为

scheme    = $2
authority = $4
path      = $5
query     = $7
fragment  = $9
     

并且,在相反的方向上,我们可以使用第5.2节的步骤7中的算法从其组件重新创建URI引用。

正则表达式可直接在Perl中使用,如

if ($uri =~ m!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!) {
    my($host,$path) = ($4,$5);
    print "$host => $path\n";
}

正则表达式量词的贪婪可能会使这种模式难以与s///一起使用,因为它会消耗尽可能多的文本,可能会超出未标记的URI边界。

更直接适用的是CPAN上提供的URI::Find模块。围绕LEFT和RIGHT就像

一样简单
#! /usr/bin/env perl

use strict;
use warnings;

use URI::Find;

my $finder = URI::Find->new(sub {
    my(undef,$found) = @_;
    "LEFT $found RIGHT";
});

while (<>) {
    $finder->find(\$_);
    print;
}

输出:

$ cat input
This is a plain text input suitable for
an answer to a question on http://stackoverflow.com

In particular, the question is available at
http://stackoverflow.com/q/15233535/123109 and the answer
at http://stackoverflow.com/a/15234378/123109

$ ./mark-uris input
This is a plain text input suitable for
an answer to a question on LEFT http://stackoverflow.com RIGHT

In particular, the question is available at
LEFT http://stackoverflow.com/q/15233535/123109 RIGHT and the answer
at LEFT http://stackoverflow.com/a/15234378/123109 RIGHT

答案 1 :(得分:2)

由于另一个问题Using regex to extract URLs from plain text with Perl,我找到了这个问题的答案。这个URL比我之前尝试的要简单得多,但似乎在我测试过的简单案例中起作用。

perl -i -pe 's,(http.*?://([^\s)\"](?!ttp:))+),left $& right,g;' myfile

答案 2 :(得分:1)

匹配网址的正则表达式很容易变得无法管理:

my @urls;
while ($body =~ m{
    (
        (ftp|https?):\/\/
        ([a-z0-9\-_]+(:[^@]+)?\@)?
        (
            ([a-z0-9\.\-]+)\.([a-z\.]{2,6})
            |
            ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})
        )
        (:[0-9]{2,5})?
        (
            [a-z0-9\.\-_/\+\%&;\:,\=\!@\(\)\[\]~\'\"]*
            [a-z0-9\.\-_/\+\%&;\:,\=\!@\(\)\[\]~]+
        )
        (\?[a-z0-9\.\-_/\+\%&;\:,\=\!@\(\)\[\]~]*)?
        (\#[a-z0-9\.\-_/\+\%&;\:,\=\!@\(\)\[\]~]*)?
    )
}gisx) {
    push @urls => $1;
}

输入Regexp::Common

use Regexp::Common qw(URI);
my @urls;

while ($body =~ m{($RE{URI}{HTTP})}gos) {
    push @urls => $1;
}

所以,要解决你的具体情况:

perl -MRegexp::Common=URI -pe 's/($RE{URI}{HTTP})/left $1 right/gos' inputfile