Как мне красиво отформатировать URL в Perl? - PullRequest
2 голосов
/ 20 сентября 2010

У меня есть несколько URL, которые я должен превратить в ссылки:

for my $url (@url_list) {
    say "<a href='$url'>$url</a>";
}

Есть ли модуль для улучшения видимости URL? Немного так:

http://www.foo.com/ → www.foo.com
http://www.foo.com/long_path → www.foo.com/lo…

Я знаю, что здесь, вероятно, подойдет простое регулярное выражение, но я избалован CPAN. :)

Ответы [ 5 ]

5 голосов
/ 20 сентября 2010

Приложение B из RFC 2396 определяет регулярное выражение, которое анализирует ссылку URI.Приспособьте это немного, чтобы получить то, что вы хотите:

#! /usr/bin/perl

use warnings;
use strict;

use 5.10.0;  # for defined-or (//)

my $uri = qr{
  ^
  (?:([^:/?\#]+):)?  # scheme = $1
  (?://([^/?\#]*))?  # authority = $2
  ([^?\#]*)          # path = $3
  (\?[^\#]*)?        # query = $4
  (\#.*)?            # fragment = $5
}x;

Приведенный выше код использует модификатор /x

Он сообщает анализатору регулярного выражения:игнорировать большинство пробелов, которые не являются ни обратной косой чертой, ни внутри класса символов.Вы можете использовать это, чтобы разбить ваше регулярное выражение на (немного) более читаемые части.Символ # также рассматривается как метасимвол, представляющий комментарий, так же, как в обычном коде Perl.

, но мы хотим сопоставить буквенные символы #, если они присутствуют, что означало, что янужно было избежать их с обратными слешами.По привычке я начал с qr/, но мне пришлось изменить разделитель из-за косых черт в шаблоне.

Несколько тестовых случаев:

my @cases = qw(
  ftp://www.foo.com.invalid/
  http://www.foo.com.invalid/
  http://www.foo.com.invalid/long_path
  http://www.foo.com.invalid/?query
  http://www.foo.com.invalid?query
  http://www.foo.com.invalid/#fragment
  http://www.foo.com.invalid#fragment
);

Немного логики

for (@cases) {
  my $nice;
  if (my($scheme,$auth,$path,@rest) = /$uri/) {
    if ($scheme eq "http" && defined $auth) {
      if (grep defined, @rest) {
        $nice = join "" => map $_ // "" => $auth, $path, @rest;
      }
      else {
        $nice = $auth
              . ($path eq "/" ? "" : $path);
      }
    }
    else {
      $nice = $_;
    }
  }

  print "$_ → $nice\n";
}

и вывод:

ftp://www.foo.com.invalid/ftp://www.foo.com.invalid/
http://www.foo.com.invalid/ → www.foo.com.invalid
http://www.foo.com.invalid/long_path → www.foo.com.invalid/long_path
http://www.foo.com.invalid/?query → www.foo.com.invalid/?query
http://www.foo.com.invalid?query → www.foo.com.invalid?query
http://www.foo.com.invalid/#fragment → www.foo.com.invalid/#fragment
http://www.foo.com.invalid#fragment → www.foo.com.invalid#fragment
4 голосов
/ 20 сентября 2010

Хитрость заключается в том, чтобы выяснить, как вы хотите печатать красивые URL-адреса каждого типа, поэтому в этом случае вам нужно указать сценарию, что делать в каждом случае:

use URI;

while( <DATA> ) {
    chomp;
    my $uri = URI->new( $_ );

    my $s = $uri->scheme;
    my $rest = do {
        if( $s =~ /(?:https?|ftp)/ ) {
            $uri->host . $uri->path_query
            }
        elsif( $s eq 'mailto' ) {
            $uri->path
            }
        elsif( ! $s ) {
            $uri
            }
        };

    print "$uri -> $rest\n";
    }

__END__
http://www.example.com/foo/bar.html
www.example.com/foo/bar.html
ftp://www.example.com
mailto:joe@example.com
https://www.example.com/foo?a=b;c=d
http://joe:password@www.example.com/login

Это приводит к:

http://www.example.com/foo/bar.html -> www.example.com/foo/bar.html
www.example.com/foo/bar.html -> www.example.com/foo/bar.html
ftp://www.example.com -> www.example.com
mailto:joe@example.com -> joe@example.com
https://www.example.com/foo?a=b;c=d -> www.example.com/foo?a=b;c=d
http://joe:password@www.example.com/login -> www.example.com/login

Если вы хотите что-то другое для определенного URL, вам просто нужно создать для него ветку и собрать нужные вам части.Обратите внимание, что URI также обрабатывает URI без схемы.

Если вам не нужны длинные строки URI для красивой печати, вы можете добавить что-то подобное, чтобы обрезать строку после стольких символов:

substr( $rest, 20 ) = '...' if length $rest > 20;

Вот решение с given, которое немного чище, но немного уродливее.Это версия Perl 5.010:

use 5.010;
use URI;

while( <DATA> ) {
    chomp;
    my $uri = URI->new( $_ );

    my $r;
    given( $uri->scheme ) {
        when( /(?:https?|ftp)/  ) { $r = $uri->host . $uri->path_query }
        when( 'mailto' )          { $r = $uri->path }       
        default                   { $r = $uri }
        }


    print "$uri -> $r\n";
    }

Это уродливее, потому что я должен повторить это назначение $r.Perl 5.14 исправит это, хотя и разрешит given возвращаемому значению.Поскольку эта стабильная версия еще не доступна, вы должны использовать экспериментальную дорожку 5.13:

use 5.013004;
use URI;

while( <DATA> ) {
    chomp;
    my $uri = URI->new( $_ );

    my $r = do {
        given( $uri->scheme ) {
            when( /(?:https?|ftp)/  ) { $uri->host . $uri->path_query }
            when( 'mailto' )          { $uri->path }        
            default                   { $uri }
            }
        };

    print "$uri -> $r\n";
    }
1 голос
/ 20 сентября 2010

Попробуйте модуль URI из cpan.

0 голосов
/ 20 сентября 2010

Я не совсем уверен, чего именно вы хотите.Я предполагаю, что вы хотите удалить http:// и иметь сокращенный URL для отображения.Если это так, вы можете сделать что-то вроде:

#!/usr/bin/perl
use strict;
use warnings;
use 5.10.1;


my @url_list = ('http://www.foo.com/','http://www.foo.com/long_path');

for my $url (@url_list) {
    (my $short = $url) =~ s!\w+://!!;
    $short =~ s!/$!!;
    $short =~ s!^(.{15}).*$!$1...!;
    say "<a href='$url'>$short</a>";
}

Выход:

<a href='http://www.foo.com/'>www.foo.com</a>
<a href='http://www.foo.com/long_path'>www.foo.com/lon...</a>
0 голосов
/ 20 сентября 2010

Частично радость Perl заключается не в том, чтобы полагаться на модули :) Мне удалось следующее решение:


#!/usr/bin/perl -w

use strict;

my @url_list = ("<a href=http://www.test.com>www.test.com</a>",
                "<a href=http://www.example.com>www.example.com</a&gt",
                "<a href=http://www.this.com>www.this.com</a&gt");

my ($protocol, $domain_name);

foreach my $url (@url_list) {
    $url =~ m|(\w+)://([^/:]+)(:\d+)?/(.*)|;
    $protocol = $1;
    $domain_name = $2;
    my ($url_part, $name_part) = split(/>/, $domain_name);
    $name_part =~ s/\<//g;
    print $protocol, "://" ,$url_part, " -> ", $name_part  , "\n";
}

Это не удивительно, и в результате я получил блуждающую < в имени доменавзял замену, чтобы удалить.Чтобы ответить на исходный вопрос, вы можете объединить LWP::Simple и HTML::LinkExtor для загрузки и анализа документов HTML из Интернета.Мощный комбо.

** Отказ от ответственности: С Ruby и Python мой Perl отстой.Извиняюсь перед пуристами за жестокое обращение с вашим языком.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...