2011-01-17 14 views
16

Jest pytanie o prawie tej samej nazwie już: What is the best regular expression to check if a string is a valid URLJaki jest complicant RFC i pracy wyrażenia regularnego, aby sprawdzić, czy ciąg jest prawidłowy adres URL

ja nie rozumiem tego stackoverflow. Wygląda na to, że potrzebuję reputacji, aby skomentować odpowiedź. Ponieważ go nie mam, nie wiem, jak powiedzieć/zapytać, czy proponowane rozwiązanie nie działa. Więc jestem zmuszony zadać nowe pytanie i poprosić o rozwiązanie w ten sposób?

AKTUALIZACJA: Wygląda na to, że Reg Exp obsługuje IPV6, a ja byłem odpowiedzialny za to, że IPv6 to , tak jak http://[2620:0:1cfe:face:b00c::3]/.

Jedyny prawdziwy problem, jaki znam, polega na tym, że akceptuje example.org: jako prawidłowy URL.

A może PHP jest winne?

/** 
    * Validate URL - RFC 3987 (IRI) 
    * 
    * https://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url 
    * 
    * @param string $str_url 
    * @return boolean 
    */ 
function is_url($str_url) 
{ 
    // RFC 3987 For absolute IRIs (internationalized): 
    return (bool) preg_match('/^[a-z](?:[-a-z0-9\+\.])*:(?:\/\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:])*@)?(?:\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4}:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+[-a-z0-9\._~!\$&\'\(\)\*\+,;=:]+)\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;[email protected]])*)(?::[0-9]*)?(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*|\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))+)(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))+)(?:\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])))(?:\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])|[\x{E000}-\x{F8FF}\x{F0000}-\x{FFFFD}|\x{100000}-\x{10FFFD}\/\?])*)?(?:\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!\$&\'\(\)\*\+,;=:@])|[\/\?])*)?$/iu',$str_url); 
} 

Oto test dla niego:

$urls=array('http://www.example.org/','http://www.example.org:80/','example.org','ftp://user:[email protected]/','http://example.org/?cat=5&test=joo','http://www.fi/?cat=5&test=joo','http://[::1]/','http://[2620:0:1cfe:face:b00c::3]/','http://[2620:0:1cfe:face:b00c::3]:80/',''); 
foreach ($urls as $a) 
{ 
    echo $a."\n"; 
    $a=is_url($a); 
    var_dump($a); 
} 

I Wyjścia:

"http://www.example.org/" bool(true) 
"http://www.example.org:80/" bool(true) 
"example.org" bool(false) 
"ftp://user:[email protected]/" bool(true) 
"http://example.org/?cat=5&test=joo" bool(true) 
"http://www.fi/?cat=5&test=joo" bool(true) 
"http://[::1]/" bool(true) 
"http://[2620:0:1cfe:face:b00c::3]/" bool(true) 
"http://[2620:0:1cfe:face:b00c::3]:80/" bool(true) 
"" bool(false) 

Więc co jest compilicant RFC i regexp działa?

+0

+1 dla użytecznego pytanie, moim zdaniem masz działającego correcty zadając nowe pytanie, ponieważ nie różni się w drugim (co jest oczywiście tylko dla IPv4). Wkrótce dostaniesz więcej powtórzeń, jeśli opublikujesz więcej tutaj w SO, a bariery o niskiej reputacji są częścią tego, co utrzymuje wysoką jakość tutaj. ;-) – Lucero

+3

Szukasz absolutnych URI, czyż nie? Ponieważ nawet pusty ciąg jest poprawnym referencją URI. – Gumbo

+1

Twój IPv6 przykład jest niepoprawny, powinien być 'http: // [2620: 0: 1 cfe: twarz: b00c :: 3]: 80 /' aby analizatory składarek rozróżniały ograniczniki szesnastkowe i opcjonalnie: 80 numerów portów. – mario

Odpowiedz

3

Po przeczytaniu RFC 3986, muszę powiedzieć, że byłem w błędzie. To regexp działa w pełni (o czym wiem). Pierwszym błędem miałem składnię adresów IPv6, są one umieszczane wokół [], a drugie dotyczyło example.org: (uwaga: końcowa podwójna kropka :). Ale jak mówi RFC, schemat może zawierać kropki, więc jest również ważny.

Tak, to jest poprawna metoda RFC, ale ludzie zazwyczaj (tak jak ja) będą musieli go zmodyfikować, aby akceptował tylko niektóre schematy.

0

Oto RFC, które można studiować: RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax. Sekcja 3.2.2 Host jest tym, czego szukasz.

Niestety wbudowany w funkcji PHP filter_var() nie obsługuje składni IPv6:

<?php 

var_dump(filter_var('http://[2620:0:1cfe:face:b00c::3]:80/', FILTER_VALIDATE_URL)); 
// Output: boolean false 
+0

Nie szukam sposobu sprawdzania poprawności adresu URL w PHP, ale zastąpienie adresów URL odnośnikami href. Och, ale to IPv6 powinno być w [], pomoże. No cóż, ten RegExp działa z tymi, ale kończy się niepowodzeniem na tej składni "example.org:". – bestis

+0

'example.org' w niewypełnionym adresie URL (brakuje części schematu (np.' Http: // ')). – Crozin

+0

Zgadzam się, example.org nie jest poprawny, ani nie jest example.org: (zauważ, że końcowa podwójna kropka). Ale to wyrażenie regularne w pytaniu mówi, że to jest ważne: przyklad.org bool (fałsz) przyklad.org: bool (prawda) – bestis

4

dobrze, jeśli spojrzeć na to, specyfikacja jest w podziale na „kawałki”. Właśnie tak chciałbym zaproponować budowanie regexu, aby było łatwiejsze do odczytania, łatwiejsze w utrzymaniu i zrozumiałe. Więc, części regex są (opcjonalnie są kursywą):

  1. Schemat
  2. Nazwa użytkownika/hasło
  3. domeny lub adres IP
  4. Port
  5. Ścieżka
  6. Zapytanie
  7. Anchor

Musimy więc zbudować pod-część regex dla każdego.

  1. Schemat:

    $scheme = "[a-z][a-z0-9+.-]*"; 
    
  2. Nazwa użytkownika/hasło:

    $username = "([^:@/](:[^:@/])[email protected])?"; 
    
  3. domeny lub adres IP:

    Teraz musimy budować na 3 możliwe gospodarze:

    1. Domain Name
    2. IPv4
    3. IPv6

    Domain Name:

    $segment = "([a-z][a-z0-9-]*?[a-z0-9])"; 
    $domain = "({$segment}\.)*{$segment}"; 
    

    IPv4:

    $segment = "([0|1][0-9]{2}|2([0-4][0-9]|5[0-5]))"; 
    $ipv4 = "({$segment}\.{$segment}\.{$segment}\.{$segment})"; 
    

    IPv6:

    $block = "([a-f0-9]{0,4})"; 
    $rawIpv6 = "({$block}:){2,8}"; 
    $ipv4sub = "(::ffff:{$ipv4})"; 
    $ipv6 = "([({$rawIpv6}|{$ipv4sub})])"; 
    

    Wreszcie:

    $host = "($domain|$ipv4|$ipv6)"; 
    
  4. port:

    $port = "(:[\d]{1,5})?"; 
    
  5. Ścieżka:

    $path = "([^?;\#]*)?"; 
    
  6. Zapytanie:

    $query = "(\?[^\#;]*)?"; 
    
  7. Kotwica:

    $anchor = "(\#.*)?"; 
    

A ostateczna regex:

$regex = "#^{$scheme}://{$username}{$host}{$port}(/{$path}{$query}{$anchor}|)$#i"; 

Zauważ, że / jest w regex, a nie część droga od drogi może być pusta.

Należy również pamiętać, że tego nie przetestowałem. Powinno działać, ale zdecydowanie wymaga potwierdzenia, że ​​każda część jest poprawna (co do czego można się spodziewać w adresie URL).

Należy również pamiętać, że jest to tylko jeden sposób. Możesz użyć innych narzędzi, które nie wymagają regexp lub biblioteki lub frameworka, które będą łatwiejsze do utrzymania na dłuższą metę.

Powodzenia

+1

Szybki wygląd wygląda na to, że zawiedzie na wielu adresach IPv6. Sprawdzanie poprawności IPv6 nie może być tak proste. Co powiesz na http: // [:: 1]/lub http: // [2620: 0: 1cfe: face: b00c :: 3] /? Ponieważ jest to normalne, że adres IPv6 jest napisany bardziej krótko. – bestis

0

Dzięki ircmaxell, ale musiałem dostosować trochę regex IPV6 do PHP, aby skompilować z preg_match.

zmieniłem:

$ipv6 = "([({$rawIpv6}|{$ipv4sub})])"; 

Do:

$ipv6 = "({$rawIpv6}|{$ipv4sub})"; 
Powiązane problemy