4

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

我想做类似下面的事情

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

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

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

但是,我不能使该正则表达式在像上面这样的 perl 构造中工作。我尝试过使用不同的分隔符/~但没有成功。

4

3 回答 3

6

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

where<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;
}

输出:

$猫输入
这是一个纯文本输入,适用于
http://stackoverflow.com 上问题的答案

特别是,该问题可在
http://stackoverflow.com/q/15233535/123109 和答案
在 http://stackoverflow.com/a/15234378/123109

$ ./mark-uris 输入
这是一个纯文本输入,适用于
LEFT http://stackoverflow.com RIGHT 上问题的答案

特别是,该问题可在
左 http://stackoverflow.com/q/15233535/123109 右和答案
在左边 http://stackoverflow.com/a/15234378/123109 右边
于 2013-03-05T21:23:06.033 回答
2

感谢另一个问题Using regex to extract URLs from plain text with Perl,我找到了这个问题的答案。该 URL 比我之前尝试的要简单得多,但在我测试过的简单情况下似乎可以工作。

perl -i -pe 's,(http.*?://([^\s)\"](?!ttp:))+),left $& right,g;' myfile
于 2013-03-05T20:47:44.313 回答
1

匹配 URL 的正则表达式很容易变得难以管理:

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
于 2013-03-05T22:29:59.677 回答