Я пытаюсь написать регулярное выражение для извлечения компонентов URL. Синтаксис можно найти здесь: RFC 3986.
Некоторые компоненты являются необязательными. Пока у меня есть:
(.+)://((.*)@)?(.+?)(:(\d*))?/((.*)\?)?((.*)#)?(.*)
Разложение:
(.+)://
соответствует схеме, за которой следует ://
. Необязательно.((.*)@)?
соответствует части полномочий пользователя с информацией о пользователе. Необязательный.(.+?)
соответствует хосту. Необязательно. (:(\d*))?
/
это и все последующее следует сделать необязательными.((.*)\?)?
соответствует части пути. Необязательный.((.*)#)?
соответствует части запроса. Необязательный.(.*)
соответствует части фрагмента. Необязательный.Как я могу улучшить это регулярное выражение, чтобы оно соответствовало RFC3986?
Забавный факт: это регулярное выражение соответствует самому себе.
Пример URL (взято из RFC): foo://example.com:8042/over/there?name=ferret#nose
Редактировать: Я забыл сбежать d
. Теперь осталось сделать все, что следует за хозяин, необязательным, включая начальный /
.
Я только что обнаружил, что мне нужно сбежать d
на маршевые номера. Так должно быть \b
В качестве примечания: в вашем шаблоне используются такие части, как .*
и .+
, которые используют точку, которая может соответствовать любому символу, включая пробелы, и сначала будет соответствовать до конца строки. Это может привести к неожиданным совпадениям. Есть ли определенный набор URL-адресов, которым вы хотите соответствовать / не соответствовать? Являются ли они частью более крупного текста или единственным URL-адресом в одной строке?
Единственное требование состоит в том, что если регулярное выражение соответствует чему-либо, это должен быть допустимый URL-адрес в соответствии с RFC-3986. Это может привести к нежелательным результатам, например: hello world foo://example.com:8042/over/there?name=ferret#nose some extra text here
приводит к совпадению. Это нежелательно, но по-прежнему является допустимым URL-адресом в соответствии с RFC (если только у вас не может быть пробелов в схеме, которые, я думаю, должны быть устранены).
Также часть фрагмента может быть любой длины, это может быть целая книга, если уж на то пошло;)
Ваше регулярное выражение отлично работает, если вы просто избегаете косых черт и, желательно, двоеточия. Результат (.+)\:\/\/(.*@)?(.+?)(:(\d*))?\/((.*)\?)?((.*)#)?(.*)
. Вот простой скрипт, показывающий, как его можно использовать для фильтрации недопустимых URI:
Обновлять После комментариев я сделал следующую модификацию:
(\:((\d*)\/))?(\/)*
. Объяснение:
\:((\d*)
соответствует двоеточию, а затем любой строке цифр.\/
после этого соответствует косой черте, которая должна быть после этой строки цифр. Это связано с тем, что порт не должен содержать никаких других символов, кроме цифр. Поэтому их нельзя найти в портовой части uri.?
.Окончательное регулярное выражение:(.+)\:\/\/(.*\@)?(.+?)(\:((\d*)\/))?(\/)*((.*)\?)?((.*)\#)?(.*)
const myRegEx = new RegExp("(.+)\:\/\/(.*\@)?(.+?)(\:((\d*)\/))?(\/)*((.*)\?)?((.*)\#)?(.*)", "g");
const allUris = [
/*Valid*/ "https://me@data.www.example.com:5050/page?query=value#element",
/*Valid*/ "foo://example.com:8042/over/there?name=ferret#nose",
/*Valid*/ "foo://example.com",
/*Not valid*/ "www.example.com"];
const allowedUris = allUris.map(uri => {
// Use the regexp to match it, then return the match
const match = uri.match(myRegEx);
return match;
});
console.log("Here are the valid URIs:");
console.log(allowedUris.join("\n\n")); // Should only print the first two URIs from the array.
Спасибо, осталась только одна проблема: foo://example.com
должно быть сопоставлено, потому что завершающий /
(и все, что следует за ним) не является обязательным. Я пытался обернуть это в ()?
, но это нарушает регулярное выражение.
Хорошо, проверьте обновленный ответ
На самом деле группы захвата сломаны :-( Вы можете попробовать распечатать их с помощью scheme: $1, user: $2, host: $3, port: $6, path: $9, query: $11, fragment: $12
Я нашел лучший способ справиться с этим, не нарушая при этом группы захвата.
(\w[\w\d\+\.-]*)://(.+@)?([^:/\?#]+)(:\d+)?(/[^\?#]*)?(\?[^#]+)?(#.*)?
Разложение:
(\w[\w\d\+\.-]*)://
соответствует допустимой схеме согласно RFC-3986.(.+@)?
соответствует информации о пользователе; то есть все до @
, по желанию.([^:/\?#]+)
соответствует хозяину; то есть все, пока не встретится :
или /
или ?
или #
.(:\d+)?
соответствует порту; то есть все цифры, необязательно(/[^\?#]*)?
соответствует пути; то есть /
плюс, необязательно, каждый символ, пока не встретится ?
или #
, необязательно.(\?[^#]+)?
соответствует запросу; то есть ?
плюс все символы, пока не встретится #
, необязательно.(#.*)?
соответствует фрагменту; то есть #
плюс все, что после, необязательно.Вывод групп захвата:
scheme: $1, user: $2, host: $3, port: $4, path: $5, query: $6, fragment: $7
Пожалуйста, приведите пример URI, который вы можете использовать