Регулярное выражение для соответствия имени хоста DNS или IP-адресу?


369

У кого-нибудь есть удобное регулярное выражение, которое будет соответствовать любому допустимому DNS-имени хоста или IP-адресу?

Легко написать тот, который работает в 95% случаев, но я надеюсь получить что-то, что хорошо протестировано, чтобы точно соответствовать последним спецификациям RFC для имен хостов DNS.

Ответы:


535

Вы можете использовать следующие регулярные выражения отдельно или объединяя их в объединенное выражение OR.

ValidIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";

ValidHostnameRegex = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$";

ValidIpAddressRegex соответствует действительным IP-адресам и допустимым именам хостов ValidHostnameRegex . В зависимости от языка, который вы используете, \ может быть экранировано с помощью \.


ValidHostnameRegex является действительным в соответствии с RFC 1123 . Первоначально RFC 952 указывал, что сегменты имени хоста не могут начинаться с цифры.

http://en.wikipedia.org/wiki/Hostname

Исходная спецификация имен хостов в RFC 952 требовала, чтобы метки не могли начинаться с цифры или с дефиса и не должны заканчиваться дефисом. Однако последующая спецификация ( RFC 1123 ) разрешила меткам имен хостов начинаться с цифр.

Valid952HostnameRegex = "^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$";

3
Здесь: stackoverflow.com/questions/4645126/… - Я объясняю, что имена, начинающиеся с цифры, также считаются действительными. Кроме того, только одна точка является сомнительной проблемой. Было бы здорово получить больше отзывов об этом.
BreakPhreak

16
Возможно, вы захотите добавить IPv6. ОП не указал, какой тип адреса. (Кстати, это можно найти здесь )
new123456 27.02.11

32
Прежде чем люди слепо используют это в своем коде, обратите внимание, что он не совсем точен. Он игнорирует RFC2181: «Сам DNS накладывает только одно ограничение на конкретные метки, которые могут использоваться для идентификации записей ресурсов. Это ограничение относится к длине метки и полному имени. Длина любой одной метки ограничена между 1 и 63 октета. Полное доменное имя ограничено 255 октетами (включая разделители). "
рубль

7
@UserControl: нелатинские (Punycoded) имена хостов должны быть сначала преобразованы в ASCII-форму ( éxämplè.com= xn--xmpl-loa1ab.com), а затем проверены.
Аликс Аксель

6
Ваше выражение имени хоста совпадает с некоторыми недопустимыми значениями: я пытался, 123.456.789.0и он говорит, что это действительное имя хоста.
Ибаррейра,

62

В имени хоста регулярное выражение smink не ограничивает длину отдельных меток в имени хоста. Каждая метка в пределах допустимого имени хоста может быть длиной не более 63 октетов.

ValidHostnameRegex = "^ ([A-Za-Z0-9] | [A-Za-Z0-9] [A-Za-Z0-9 \ -] {0,61} [A-Za-Z0-9]) \
(\ ([A-Za-Z0-9] |. [A-Za-Z0-9] [A-Za-Z0-9 \ -] {0,61} [A-Za-Z0-9])) * $»

Обратите внимание, что обратная косая черта в конце первой строки (выше) - это синтаксис оболочки Unix для разбиения длинной строки. Это не часть самого регулярного выражения.

Вот только одно регулярное выражение в одной строке:

^ ([A-Za-Z0-9] | [A-Za-Z0-9] [A-Za-Z0-9 \ -] {0,61} [A-Za-Z0-9]) (\. ([A-Za-Z0-9] | [A-Za-Z0-9] [A-Za-Z0-9 \ -] {0,61} [A-Za-Z0-9])) * $

Вы также должны отдельно проверить, что общая длина имени хоста не должна превышать 255 символов . Для получения дополнительной информации, пожалуйста, обратитесь к RFC-952 и RFC-1123.


6
Отличная схема размещения. Возможно, это зависит от реализации регулярных выражений в своем языке, но для JS его можно немного скорректировать, чтобы сделать его более коротким, не теряя ничего:/^[a-z\d]([a-z\d\-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d\-]{0,61}[a-z\d])?)*$/i
Точка с запятой

31

Чтобы сопоставить действительный IP-адрес, используйте следующее регулярное выражение:

(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}

вместо:

([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\.([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])){3}

объяснение

Многие движки регулярных выражений соответствуют первой возможности в ORпоследовательности. Например, попробуйте следующее регулярное выражение:

10.48.0.200

Тестовое задание

Проверьте разницу между хорошим и плохим


5
Не забывайте, что начало ^ и конец $ или что-то вроде 0.0.0.999 или 999.0.0.0 тоже будут совпадать. ;)
Андреас

1
yes для проверки правильности строки начинаются ^ и end $, но если вы ищете IP в тексте, не используйте его.
Албан

Непреднамеренная «нежадность», которую вы идентифицируете, применима и к другим решениям имен хостов. Стоит добавить это к вашему ответу, так как остальные не будут соответствовать полному имени хоста. например, ([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*против([a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9])(\.([a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])|[a-zA-Z0-9]))*
ergohack

РЕДАКТИРОВАТЬ: В приведенном выше, используйте +в конце вместо того, *чтобы увидеть сбой.
ergohack

5

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

Для имени хоста - простой ответ, на примере egrep здесь - http: //www.linuxinsight.com/how_to_grep_for_ip_addresses_using_the_gnu_egrep_utility.html

egrep '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}'

Хотя в этом случае не учитываются такие значения, как 0 в первом октете, а также значения, превышающие 254 (IP-адрес) или 255 (маска сети). Может быть, дополнительное заявление if поможет.

Что касается допустимого имени хоста DNS, при условии, что вы проверяете только имена хостов в Интернете (а не в интрасети), я написал следующий фрагмент кода shell / php, но он должен быть применим в качестве любого регулярного выражения.

Сначала зайдите на сайт ietf, скачайте и проанализируйте список законных доменных имен уровня 1:

tld=$(curl -s http://data.iana.org/TLD/tlds-alpha-by-domain.txt |  sed 1d  | cut -f1 -d'-' | tr '\n' '|' | sed 's/\(.*\)./\1/')
echo "($tld)"

Это должно дать вам хороший кусок кода, который проверяет законность верхнего доменного имени, например .com .org или .ca

Затем добавьте первую часть выражения в соответствии с указаниями, приведенными здесь - http://www.domainit.com/support/faq.mhtml?category=Domain_FAQ&question=9 (любая буквенно-цифровая комбинация и символ «-» не должны быть в тире начало или конец октета.

(([a-z0-9]+|([a-z0-9]+[-]+[a-z0-9]+))[.])+

Затем соберите все это вместе (пример PHP preg_match):

$pattern = '/^(([a-z0-9]+|([a-z0-9]+[-]+[a-z0-9]+))[.])+(AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN|XN|XN|XN|XN|XN|XN|XN|XN|XN|XN|YE|YT|YU|ZA|ZM|ZW)[.]?$/i';

    if (preg_match, $pattern, $matching_string){
    ... do stuff
    }

Вы также можете добавить оператор if, чтобы проверить, что проверяемая вами строка короче 256 символов - http://www.ops.ietf.org/lists/namedroppers/namedroppers.2003/msg00964.html.


1
-1, потому что это соответствует фиктивным IP-адресам, таким как «999.999.999.999».
bdesham

1
«Хотя в этом случае не учитываются такие значения, как 0 в первом октете, и значения, превышающие 254 (IP-адрес) или 255 (маска сети)».
Алексей Волков

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

3

Стоит отметить, что есть библиотеки для большинства языков, которые делают это для вас, часто встроенные в стандартную библиотеку. И эти библиотеки, вероятно, будут обновляться гораздо чаще, чем код, который вы скопировали из ответа Stack Overflow четыре года назад и о котором забыли. И, конечно, они также обычно разбирают адрес в какую-то удобную форму, а не просто дают вам совпадение с группой групп.

Например, обнаружение и анализ IPv4 в (POSIX) C:

#include <arpa/inet.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  for (int i=1; i!=argc; ++i) {
    struct in_addr addr = {0};
    printf("%s: ", argv[i]);
    if (inet_pton(AF_INET, argv[i], &addr) != 1)
      printf("invalid\n");
    else
      printf("%u\n", addr.s_addr);
  }
  return 0;
}

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

Например, в Python:

>>> import ipaddress
>>> import re
>>> msg = "My address is 192.168.0.42; 192.168.0.420 is not an address"
>>> for maybeip in re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', msg):
...     try:
...         print(ipaddress.ip_address(maybeip))
...     except ValueError:
...         pass

2
def isValidHostname(hostname):

    if len(hostname) > 255:
        return False
    if hostname[-1:] == ".":
        hostname = hostname[:-1]   # strip exactly one dot from the right,
                                   #  if present
    allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
    return all(allowed.match(x) for x in hostname.split("."))

Не могли бы вы объяснить это регулярное выражение? Что именно (?! -), (? <! -) означает?
Скот

1
@Scit, они уверены, что он не начинается и не заканчивается символом «-», если ваш движок регулярных выражений позволяет их использовать. Например, из Python или из Perl .
YLearn

1

Я думаю, что это лучшее регулярное выражение проверки IP. пожалуйста, проверьте это один раз !!!

^(([01]?[0-9]?[0-9]|2([0-4][0-9]|5[0-5]))\.){3}([01]?[0-9]?[0-9]|2([0-4][0-9]|5[0-5]))$


1

Это работает для действительных IP-адресов:

regex = '^([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])[.]([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])[.]([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])[.]([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-5][0-5])$'

1
/^(?:[a-zA-Z0-9]+|[a-zA-Z0-9][-a-zA-Z0-9]+[a-zA-Z0-9])(?:\.[a-zA-Z0-9]+|[a-zA-Z0-9][-a-zA-Z0-9]+[a-zA-Z0-9])?$/

0

Вот регулярное выражение, которое я использовал в Ant для получения IP-адреса прокси-сервера или имени хоста из ANT_OPTS. Это использовалось для получения IP-адреса прокси, чтобы я мог выполнить тест Ant «isreachable» перед настройкой прокси для разветвленной JVM.

^.*-Dhttp\.proxyHost=(\w{1,}\.\w{1,}\.\w{1,}\.*\w{0,})\s.*$

Это \wверно, он не будет захватывать IP, только имя хоста в определенных ситуациях.
Ярон

0

Я обнаружил, что это работает очень хорошо для IP-адресов. Он проверяется как верхний ответ, но также гарантирует, что ip изолирован, поэтому текст или больше цифр / десятичных чисел не идут после или перед ip.

(? <! \ S)? (: (: \ Д | [1-9] \ д | 1 \ д \ д | 2 [0-4] \ д | 25 [0-5]) \ Ъ |. \ б) {7} (?! \ S)


Я очень старался, но я не мог понять 2 вещи здесь. 1. \ b указывает границу слова. Почему мы используем \ b? какая граница? и 2. Почему это работает только для {7} Из того, что я понял, я думаю, что это должно быть {4}, но это не работает. При желании вы можете рассказать о том, почему вы используете блоки без захвата.
Сричакрадхар

0
AddressRegex = "^(ftp|http|https):\/\/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5})$";

HostnameRegex =  /^(ftp|http|https):\/\/([a-z0-9]+\.)?[a-z0-9][a-z0-9-]*((\.[a-z]{2,6})|(\.[a-z]{2,6})(\.[a-z]{2,6}))$/i

это повторно используются только для проверки этого типа

работать только если http://www.kk.com http://www.kk.co.in

не работает для

http://www.kk.com/ http: //www.kk.co.in.kk

http://www.kk.com/dfas http://www.kk.co.in/


0

попробуй это:

((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

это работает в моем случае.


0

Что касается IP-адресов, кажется, что есть некоторые споры о том, включать ли ведущие нули. Когда-то это было обычной практикой и общепринятым, поэтому я бы сказал, что они должны быть отмечены как действительные, независимо от текущих предпочтений. Существует также некоторая двусмысленность относительно того, должен ли текст до и после строки проверяться, и, опять же, я думаю, что это должно быть. 1.2.3.4 является действительным IP, но 1.2.3.4.5 - нет, и ни часть 1.2.3.4, ни часть 2.3.4.5 не должны приводить к совпадению. Некоторые из проблем могут быть обработаны с помощью этого выражения:

grep -E '(^|[^[:alnum:]+)(([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])([^[:alnum:]]|$)' 

Неудачной частью здесь является тот факт, что часть регулярного выражения, которая проверяет октет, повторяется, как и во многих предлагаемых решениях. Хотя это лучше, чем для экземпляров шаблона, повторение можно полностью исключить, если в используемом регулярном выражении поддерживаются подпрограммы. В следующем примере эти функции -Pвключаются с помощью переключателя, grepа также используются функции прогнозирования и прогнозирования. (Я выбрал имя функции «o» для октета. Я мог бы использовать «octet» в качестве имени, но хотел быть кратким.)

grep -P '(?<![\d\w\.])(?<o>([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]))(\.\g<o>){3}(?![\d\w\.])'

Обработка точки может на самом деле создавать ложные отрицания, если IP-адреса находятся в файле с текстом в форме предложений, поскольку период может следовать, не будучи частью точечной нотации. Вариант вышеупомянутого исправит это:

grep -P '(?<![\d\w\.])(?<x>([0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]))(\.\g<x>){3}(?!([\d\w]|\.\d))'

0
>>> my_hostname = "testhostn.ame"
>>> print bool(re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", my_hostname))
True
>>> my_hostname = "testhostn....ame"
>>> print bool(re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", my_hostname))
False
>>> my_hostname = "testhostn.A.ame"
>>> print bool(re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", my_hostname))
True

0

Новая сетевая структура имеет неудачные инициализаторы для struct IPv4Address и struct IPv6Address, которые очень легко обрабатывают часть IP-адреса. Делать это в IPv6 с регулярным выражением сложно со всеми правилами сокращения.

К сожалению, у меня нет элегантного ответа на имя хоста.

Обратите внимание, что Network Framework является последней, поэтому она может заставить вас скомпилировать для последних версий ОС.

import Network
let tests = ["192.168.4.4","fkjhwojfw","192.168.4.4.4","2620:3","2620::33"]

for test in tests {
    if let _ = IPv4Address(test) {
        debugPrint("\(test) is valid ipv4 address")
    } else if let _ = IPv6Address(test) {
        debugPrint("\(test) is valid ipv6 address")
    } else {
        debugPrint("\(test) is not a valid IP address")
    }
}

output:
"192.168.4.4 is valid ipv4 address"
"fkjhwojfw is not a valid IP address"
"192.168.4.4.4 is not a valid IP address"
"2620:3 is not a valid IP address"
"2620::33 is valid ipv6 address"

-1

как насчет этого?

([0-9]{1,3}\.){3}[0-9]{1,3}

Как и 9999999999.0.0.9999999999 :) Но для большинства программистов этого краткого подхода будет достаточно.
Андреас

3
-1, потому что это соответствует бессмысленным IP-адресам (как отмечает @Shebuka).
bdesham

-1

на php: filter_var(gethostbyname($dns), FILTER_VALIDATE_IP) == true ? 'ip' : 'not ip'


2
Хотя этот код может ответить на вопрос, в общем случае объяснение рядом с кодом делает ответ гораздо более полезным. Пожалуйста, отредактируйте свой ответ и предоставьте некоторый контекст и объяснение.
user4642212

И, если я не ошибаюсь, FILTER_VALIDATE_IP - это значение только для PHP.
ДонГар

-2

Проверка имен хостов, таких как ... mywebsite.co.in, thangaraj.name, 18thangaraj.in, thangaraj106.in и т. Д.,

[a-z\d+].*?\\.\w{2,4}$

3
-1. ОП попросил что-то «хорошо протестированное, чтобы точно соответствовать последним спецификациям RFC», но это не соответствует, например, * .museum, тогда как оно будет соответствовать * .foo. Вот список действительных TLD.
bdesham

Я не уверен, что это хорошая идея - ставить плюс внутри класса символов (квадратные скобки), кроме того, есть TLD с 5 буквами ( например, .expert ).
Ярон

Лучший способ добиться этого с помощью RFC - это использовать системные / языковые функции. inet_atonдостаточно хорош
м3нда

-2

Я думал об этом простом шаблоне сопоставления регулярных выражений для сопоставления IP-адресов \ d + [.] \ D + [.] \ D + [.] \ D +


1111.1.1.1 не является действительным ip. Там нет никакого способа по-настоящему проверить формат IP, если вы не заботитесь о подсетях. Вы должны по крайней мере позаботиться о количестве появлений с чем-то вроде ^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}и, конечно, это не будет правильным способом. Если у вас есть язык для написания скрипта, вы наверняка получите доступ к его сетевым функциям. Лучший способ проверить РЕАЛЬНЫЙ ip - это сказать системе конвертировать и ip в правильный формат, а затем проверить на true / false. В случае с Python я использую socket.inet_aton(ip). Случай PHP вам нужен inet_aton($ip).
m3nda

Пользователи Python могут посмотреть здесь: gist.github.com/erm3nda/f25439bba66931d3ca9699b2816e796c
m3nda
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.