Регулярное выражение для извлечения компонентов URL

Я пытаюсь написать регулярное выражение для извлечения компонентов URL. Синтаксис можно найти здесь: RFC 3986.

Некоторые компоненты являются необязательными. Пока у меня есть:

(.+)://((.*)@)?(.+?)(:(\d*))?/((.*)\?)?((.*)#)?(.*)

Разложение:

  • (.+):// соответствует схеме, за которой следует ://. Необязательно.
  • ((.*)@)? соответствует части полномочий пользователя с информацией о пользователе. Необязательный.
  • (.+?) соответствует хосту. Необязательно. Здесь есть проблема, когда эта группа также будет соответствовать является необязательным портом.
  • (:(\d*))?должен соответствует порту.
  • / это и все последующее следует сделать необязательными.
  • ((.*)\?)? соответствует части пути. Необязательный.
  • ((.*)#)? соответствует части запроса. Необязательный.
  • (.*) соответствует части фрагмента. Необязательный.

Как я могу улучшить это регулярное выражение, чтобы оно соответствовало RFC3986?

Забавный факт: это регулярное выражение соответствует самому себе.

Пример URL (взято из RFC): foo://example.com:8042/over/there?name=ferret#nose

Редактировать: Я забыл сбежать d. Теперь осталось сделать все, что следует за хозяин, необязательным, включая начальный /.

Пожалуйста, приведите пример URI, который вы можете использовать

AlphaHowl 10.04.2022 16:33

Я только что обнаружил, что мне нужно сбежать d на маршевые номера. Так должно быть \b

PinkTurtle 10.04.2022 16:39

В качестве примечания: в вашем шаблоне используются такие части, как .* и .+, которые используют точку, которая может соответствовать любому символу, включая пробелы, и сначала будет соответствовать до конца строки. Это может привести к неожиданным совпадениям. Есть ли определенный набор URL-адресов, которым вы хотите соответствовать / не соответствовать? Являются ли они частью более крупного текста или единственным URL-адресом в одной строке?

The fourth bird 10.04.2022 17:09

Единственное требование состоит в том, что если регулярное выражение соответствует чему-либо, это должен быть допустимый URL-адрес в соответствии с RFC-3986. Это может привести к нежелательным результатам, например: hello world foo://example.com:8042/over/there?name=ferret#nose some extra text here приводит к совпадению. Это нежелательно, но по-прежнему является допустимым URL-адресом в соответствии с RFC (если только у вас не может быть пробелов в схеме, которые, я думаю, должны быть устранены).

PinkTurtle 10.04.2022 17:19

Также часть фрагмента может быть любой длины, это может быть целая книга, если уж на то пошло;)

PinkTurtle 10.04.2022 17:23
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
0
5
30
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваше регулярное выражение отлично работает, если вы просто избегаете косых черт и, желательно, двоеточия. Результат (.+)\:\/\/(.*@)?(.+?)(:(\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 должно быть сопоставлено, потому что завершающий / (и все, что следует за ним) не является обязательным. Я пытался обернуть это в ()?, но это нарушает регулярное выражение.

PinkTurtle 10.04.2022 16:53

Хорошо, проверьте обновленный ответ

AlphaHowl 10.04.2022 17:10

На самом деле группы захвата сломаны :-( Вы можете попробовать распечатать их с помощью scheme: $1, user: $2, host: $3, port: $6, path: $9, query: $11, fragment: $12

PinkTurtle 10.04.2022 19:10

Я нашел лучший способ справиться с этим, не нарушая при этом группы захвата.

(\w[\w\d\+\.-]*)://(.+@)?([^:/\?#]+)(:\d+)?(/[^\?#]*)?(\?[^#]+)?(#.*)?

Разложение:

  • (\w[\w\d\+\.-]*):// соответствует допустимой схеме согласно RFC-3986.
  • (.+@)? соответствует информации о пользователе; то есть все до @, по желанию.
  • ([^:/\?#]+) соответствует хозяину; то есть все, пока не встретится : или / или ? или #.
  • (:\d+)? соответствует порту; то есть все цифры, необязательно
  • (/[^\?#]*)? соответствует пути; то есть / плюс, необязательно, каждый символ, пока не встретится ? или #, необязательно.
  • (\?[^#]+)? соответствует запросу; то есть ? плюс все символы, пока не встретится #, необязательно.
  • (#.*)? соответствует фрагменту; то есть # плюс все, что после, необязательно.

Вывод групп захвата:

scheme: $1, user: $2, host: $3, port: $4, path: $5, query: $6, fragment: $7

Другие вопросы по теме