URL Regex не работает - PullRequest
       4

URL Regex не работает

3 голосов
/ 31 августа 2010

Используя Perl, я пытаюсь проанализировать кучу файлов XML и пытаюсь найти любой URL-адрес в XML и распечатать его. Мое регулярное выражение, похоже, не работает и не возвращает никаких совпадений. Чего мне не хватает?

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 ]

12 голосов
/ 31 августа 2010

используйте Regexp :: Common

use Regexp::Common qw /URI/;

while (<>) {
    /$RE{URI}{HTTP}/       and  print "Contains an HTTP URI.\n";
}
8 голосов
/ 31 августа 2010

Ваш код состоит из семи разных оттенков неправильно:

  • Вы не должны использовать регулярные выражения для разбора XML (см. Этот вопрос )
  • local, вероятно, не следует использовать таким образом, вы, вероятно, хотите my
  • Переменные $&, $' и $ ` не должны использоваться (вместо этого используйте captures )
  • У тебя ужасный отступ
  • $inputLine = $_[1]; получает второй аргумент функции (что является первым?)
  • если вы собираетесь использовать регулярное выражение, вы должны использовать /g модификатор регулярного выражения , а не бросать свой собственный код с несколькими совпадениями
  • Ваше регулярное выражение захватывает то, что не должно (используйте (?:) для группировки, а не ())

Вот как я написал бы ваш код, если бы мне было все равно, что я получу то, что не должен, и могу пропустить то, что хочу (потому что регулярное выражение не может быть достаточно умным для анализа 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 голосов
/ 31 августа 2010

Используйте модули URI :: Find и URI :: Find :: Schemeless , доступные в CPAN. Например

#! /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
0 голосов
/ 31 августа 2010

Несколько комментариев, напрямую не связанных с вашим вопросом, но с вашим кодом.

  1. Я не понимаю, почему вы используете local в предоставленном вами контексте.У меня такое чувство, что вы должны использовать my вместо local.
  2. $inputLine = $_[1] на самом деле означает, что вы хотите присвоить второй аргумент, который вы передаете findURL, на $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, конечно же, предлагает лучшее регулярное выражение.

0 голосов
/ 31 августа 2010

Я думаю, это то, что вы считаете классом персонажей. По некоторым причинам это компилируется, но выходные данные отладки показывают кое-что любопытное, когда я изолировал класс символов.

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}.-]
  • Еще одна интересная вещь, которую я обнаружил, заключается в том, что в ']' интерпретируется как литерал квадратное закрытие, где класс символов не открыт. Таким образом, вам нужно только избежать его, чтобы избежать окончания класса символов и, следовательно, включить его. И наоборот, '[[' будет включать '[' в класс символов, поэтому нет смысла избегать '[', если только вне класса символов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...