Вопрос о проверке URL-адресов с помощью регулярных выражений [закрыт]

У меня есть следующее регулярное выражение, которое отлично справляется с сопоставлением URL-адресов:

((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+-=\\\.&]*)`

Однако он не обрабатывает URL-адреса без префикса, т.е. stackoverflow.com или www.google.com не совпадают. Кто-нибудь знает, как я могу изменить это регулярное выражение, чтобы не заботиться о том, есть ли префикс или нет?


РЕДАКТИРОВАТЬ: Мой вопрос слишком расплывчатый? Нужно ли больше подробностей?


(((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\)))?[\w\d:#@%/;$()~_?\+-=\\\.&]*)

Я добавил ()? вокруг протоколов, предложенных Винко Врсалович, но теперь регулярное выражение будет соответствовать практически любой строке, если она содержит допустимые символы URL.

Моя реализация этого заключается в том, что у меня есть база данных, которой я управляю содержимым, и у нее есть поле, которое имеет либо обычный текст, номер телефона, URL-адрес или адрес электронной почты. Я искал простой способ проверить ввод, чтобы я мог его правильно отформатировать, т.е. создание тегов привязки для URL-адреса/электронной почты и форматирование номера телефона так, как я отформатировал другие номера на всем сайте. Какие-либо предложения?


person Anders    schedule 22.10.2008    source источник
comment
Должен добавить, я не предлагал вам делать это на самом деле, а просто показывал вам, что это регулярное выражение почти бесполезно без этой части.   -  person Vinko Vrsalovic    schedule 22.10.2008
comment
Что значит действительный в данном контексте? Что вы должны угадать, что это?   -  person Vinko Vrsalovic    schedule 22.10.2008


Ответы (5)


Приведенное ниже регулярное выражение взято из замечательного 1/279-4458937-3540756?ie= Книга UTF8&s=books&qid=1224694042&sr=8-1" rel="nofollow noreferrer">Mastering Regular Expressions. Если вы не знакомы с режимом свободного пробела/комментариев, я предлагаю вам ознакомиться с этим.

\b
# Match the leading part (proto://hostname, or just hostname)
(
    # ftp://, http://, or https:// leading part
    (ftp|https?)://[-\w]+(\.\w[-\w]*)+
  |
    # or, try to find a hostname with our more specific sub-expression
    (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains
    # Now ending .com, etc. For these, require lowercase
    (?-i: com\b
        | edu\b
        | biz\b
        | gov\b
        | in(?:t|fo)\b # .int or .info
        | mil\b
        | net\b
        | org\b
        | name\b
        | coop\b
        | aero\b
        | museum\b
        | [a-z][a-z]\b # two-letter country codes
    )
)

# Allow an optional port number
( : \d+ )?

# The rest of the URL is optional, and begins with / . . . 
(
     /
     # The rest are heuristics for what seems to work well
     [^.!,?;"'<>()\[\]{}\s\x7F-\xFF]*
     (?:
        [.!,?]+  [^.!,?;"'<>()\[\]{}\s\x7F-\xFF]+
     )*
)?

Кратко объясним это регулярное выражение (для полного объяснения получите книгу) - URL-адреса имеют одну или несколько частей, разделенных точками, заканчивающихся либо ограниченным списком конечных битов, либо двухбуквенным кодом страны (.uk .fr ...). Кроме того, части могут иметь любые буквенно-цифровые символы или дефисы «-», но дефисы не могут быть первыми или последними символами частей. Потом может быть номер порта, а потом все остальное.

Чтобы извлечь это с веб-сайта, перейдите по адресу http://regex.info/listing.cgi?ed=3&p=207 Это со страницы 207 3-го издания.

И на странице написано «Авторское право © Джеффри Фридл, 2008», поэтому я не уверен, каковы точные условия использования, но я ожидаю, что если у вас есть книга, вы можете ее использовать, так что ... я надеюсь, что я не нарушая правил размещения его здесь.

person Hamish Downer    schedule 22.10.2008

Если вы прочитали раздел 5 спецификации URL (http://www.isi.edu/in-notes/rfc1738.txt), вы увидите, что синтаксис URL является минимальным:

scheme ':' schemepart

где схема — это 1 или более символов, а схема — 0 или более символов. Поэтому, если у вас нет двоеточия, у вас нет URL-адреса.

Тем не менее, /users/ не волнует, дали ли они вам URL-адрес, для них он выглядит так. Итак, вот что я делаю:

ПЕРЕД проверкой, если в нем нет двоеточия, добавьте http://, а затем запустите его через любой валидатор, который вы хотите. Это превращает любое законное имя хоста (которое, в конце концов, может не включать информацию о домене) во что-то похожее на URL-адрес.

frob  ->  http://frob

(Почти) единственное правило для основной части состоит в том, что она не может начинаться с цифры, если не содержит точек. Теперь есть определенные проверки, которые должны быть выполнены для конкретных схем, которые не выполняет ни одно из приведенных до сих пор регулярных выражений. Но соответствие спецификации, вероятно, не то, что вы хотите «подтвердить». Поэтому DNS-запрос в части имени хоста может быть полезен, но если вы не используете тот же преобразователь в том же контексте, что и ваш пользователь, он не будет работать во всех случаях.

person caskey    schedule 22.10.2008

Ваше регулярное выражение соответствует всему, что начинается с одного из этих протоколов, включая множество вещей, которые не могут быть существующими URL-адресами, если вы ослабите часть протокола (сделав ее необязательной с помощью ?), то вы просто сопоставите почти все, включая пустую строку .

Другими словами, он отлично справляется с сопоставлением URL-адресов, потому что он соответствует почти всем, начинающимся с http://,https://,ftp:// и так далее. Ну, это также соответствует ftp:\\ и ms-help://, но давайте проигнорируем это.

Это может иметь смысл, в зависимости от фактического использования, потому что другой подход регулярных выражений к внесению допустимых доменов в белый список становится непригодным для сопровождения достаточно быстро, но делать необязательную часть протокола не имеет смысла.

Пример (с частью расслабленного протокола):

>>> r = re.compile('(((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\))+)?[\w\d:#@%/;$()~_?\+-=\\\.&]*)')
>>> r.search('oompaloompa_is_not_an_ur%&%%l').groups()[0]
'oompaloompa_is_not_an_ur%&%%l' #Matches!
>>> r.search('oompaloompa_isdfjakojfsdi.sdnioknfsdjknfsdjk.fsdnjkfnsdjknfsdjk').groups()[0]
'oompaloompa_isdfjakojfsdi.sdnioknfsdjknfsdjk.fsdnjkfnsdjknfsdjk' #Matches!
>>>                             

Учитывая ваше редактирование, я предлагаю вам либо заставить пользователя выбрать, что он добавляет, добавив столбец enum, либо создать более простое регулярное выражение, которое будет проверять хотя бы точку, помимо допустимых символов и, возможно, некоторых общих доменов.

Третий вариант, который будет ОЧЕНЬ МЕДЛЕННЫМ и будет использоваться только тогда, когда проверка URL-адреса ДЕЙСТВИТЕЛЬНО-ДЕЙСТВИТЕЛЬНО ВАЖНА, — это фактический доступ к URL-адресу и выполнение запроса HEAD для него, если вы получаете хост не найден или ошибка, о которой вы знаете, что она недействительна. Для электронных писем вы можете попробовать и посмотреть, существует ли хост MX и открыт ли порт 25. Если оба не работают, это будет обычный текст. (это я тоже не предлагаю)

person Vinko Vrsalovic    schedule 22.10.2008

Вы можете заключить часть префикса в скобки и сопоставить 0 или 1 вхождение

(((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\))+)?

Таким образом, все регулярное выражение станет

(((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\))+)?[\w\d:#@%/;$()~_?\+-=\\\.&]*)

Проблема в том, что оно будет соответствовать более или менее любому слову. Например, "тест" также будет совпадением.

Где вы собираетесь использовать это регулярное выражение? Вы пытаетесь проверить имя хоста или пытаетесь найти имена хостов внутри абзаца?

person marto    schedule 22.10.2008
comment
Я обновил свой пост с моим намерением для этого кода. - person Anders; 22.10.2008

Просто используйте:

.*

то есть совпадать со всем.

То, что вы хотите сопоставить, это просто имена хостов, а не URL (технически).

Не существует структуры, которую можно было бы использовать для окончательной идентификации имен хостов. Возможно, вы могли бы искать вещи, оканчивающиеся на «.com», но тогда вы пропустите все .co.uk, net, .org и т. д.

Редактировать:

Другими словами: если вы удалите требование, чтобы URL-подобные вещи начинались с протокола, у вас не будет ничего для сопоставления. В зависимости от того, для чего вы используете регулярное выражение:

  1. Рассматривайте все как URL
  2. Сохраняйте требование к протоколу
  3. Хак проверяет общие окончания имен хостов (например, .com .net .org) и признает, что некоторые из них вы пропустите.
person Douglas Leeder    schedule 22.10.2008
comment
Вы предлагаете заменить содержимое квадратных скобок на .*? - person Anders; 22.10.2008
comment
не заменять все регулярное выражение. Или лучше просто удалите регулярное выражение и относитесь ко всему как к URL-адресу. - person Douglas Leeder; 22.10.2008