Что такое сложное и рабочее регулярное выражение RFC для проверки того, является ли строка допустимым URL-адресом
Уже есть вопрос с почти таким же названием: Какое лучшее регулярное выражение для проверки того, является ли строка допустимым URL-адресом
Я не понимаю этого стекового потока. Похоже, мне нужна репутация, чтобы прокомментировать ответ. Поскольку у меня его нет, я не знаю, как сказать/спросить, что предлагаемое решение, похоже, не работает. Итак, я вынужден задать новый вопрос и попросить решение таким образом?
ОБНОВЛЕНИЕ: Похоже, что Reg Exp поддерживает IPV6, и я был виноват, так как IPv6 должен выглядеть как http://[2620:0:1cfe:лицо:b00c::3]/.
Так что единственная реальная проблема, которую я знаю сейчас, заключается в том, что он принимает example.org : как действительный URL-адрес.
Или виноват PHP?
/**
* 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}!\$&\'\(\)\*\+,;=@])*)(?::[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);
}
Вот тест для этого:
$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);
}
И это выводит:
"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)
Итак, что такое компилятор RFC и рабочее регулярное выражение?
4 answers
Ну, если вы посмотрите на это, спецификация разбита на "куски". Вот как я бы предложил создать регулярное выражение, чтобы его было легче читать, более удобным для обслуживания и понятным. Итак, части регулярного выражения (необязательно выделены курсивом):
- Схема
- Имя пользователя/Пароль
- Домен Или IP-Адрес
- Порт
- Путь
- Запрос
- Якорь
Итак, нам нужно создайте подраздел регулярного выражения для каждого из них.
-
Схема:
$scheme = "[a-z][a-z0-9+.-]*";
-
Имя пользователя/пароль:
$username = "([^:@/](:[^:@/])?@)?";
-
Домен или IP-адрес:
Теперь нам нужно создать 3 возможных хоста:
- Доменное имя
- IPv4
- IPv6
Доменное имя:
$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})])";
Наконец-то:
$host = "($domain|$ipv4|$ipv6)";
-
Порт:
$port = "(:[\d]{1,5})?";
-
Путь:
$path = "([^?;\#]*)?";
-
Запрос:
$query = "(\?[^\#;]*)?";
-
Якорь:
$anchor = "(\#.*)?";
И последнее регулярное выражение:
$regex = "#^{$scheme}://{$username}{$host}{$port}(/{$path}{$query}{$anchor}|)$#i";
Обратите внимание, что /
находится в регулярном выражении, а не в части пути, так как путь может быть пустым.
Также обратите внимание, что я не проверял это. Он должен работать, но определенно нуждается в подтверждении правильности каждой части (что касается того, чего ожидать в url).
Также обратите внимание, что это только один из способов сделать это. Вы можете использовать другие инструменты, которым не нужны регулярные выражения, или библиотеку или фреймворк, которые в долгосрочной перспективе будет проще поддерживать.
Желаю удачи
Прочитав RFC 3986, я должен сказать, что был неправ. Это регулярное выражение полностью работает (это я знаю). Первой ошибкой, с которой я столкнулся, был синтаксис адресов IPv6, они расположены вокруг [], а вторая была о example.org : (обратите внимание на двойную точку в конце:). Но, как сказано в RFC, схема может содержать точки, поэтому она также действительна.
Так что это допустимый способ RFC для этого, но людям обычно (как и мне) нужно будет изменить его, чтобы принимать только некоторые схемы.
Вот RFC, который вы можете изучить: RFC 3986 - Единый идентификатор ресурса (URI): Общий синтаксис. Раздел 3.2.2 Host
- это то, что вы ищете.
К сожалению, встроенная функция PHP filter_var()
не поддерживает синтаксис IPv6:
<?php
var_dump(filter_var('http://[2620:0:1cfe:face:b00c::3]:80/', FILTER_VALIDATE_URL));
// Output: boolean false
Спасибо ircmaxell, но мне пришлось немного скорректировать регулярное выражение IPV6 для PHP для компиляции с preg_match.
Я изменил:
$ipv6 = "([({$rawIpv6}|{$ipv4sub})])";
Кому:
$ipv6 = "({$rawIpv6}|{$ipv4sub})";