Suivi de Expression régulière pour faire correspondre le nom d’hôte ou l’adresse IP? et en utilisant Restrictions sur les noms d'hôtes valides comme référence, quel est le moyen le plus lisible et le plus concis de faire correspondre/valider un nom d'hôte/fqdn (nom de domaine complet) en Python? J'ai répondu avec ma tentative ci-dessous, les améliorations sont les bienvenues.
import re
def is_valid_hostname(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("."))
assure que chaque segment
Cela évite également les doubles négatifs (not disallowed
), et si hostname
se termine par un .
, ce n'est pas grave. Il échouera (et devrait échouer) si hostname
se termine par plus d'un point.
Voici une version un peu plus stricte de La réponse de Tim Pietzcker avec les améliorations suivantes:
[0-9]
au lieu de \d
).import re
def is_valid_hostname(hostname):
if hostname[-1] == ".":
# strip exactly one dot from the right, if present
hostname = hostname[:-1]
if len(hostname) > 253:
return False
labels = hostname.split(".")
# the TLD must be not all-numeric
if re.match(r"[0-9]+$", labels[-1]):
return False
allowed = re.compile(r"(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(label) for label in labels)
Per The Old New Thing , la longueur maximale d’un nom DNS est de 253 caractères. (Un seul est autorisé jusqu'à 255 octets, mais 2 d'entre eux sont consommés par le codage.)
import re
def validate_fqdn(dn):
if dn.endswith('.'):
dn = dn[:-1]
if len(dn) < 1 or len(dn) > 253:
return False
ldh_re = re.compile('^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$',
re.IGNORECASE)
return all(ldh_re.match(x) for x in dn.split('.'))
On pourrait argumenter en faveur de l'acceptation de noms de domaine vides ou non, en fonction du but recherché.
Complément à la réponse @TimPietzcker. Le trait de soulignement est un nom d’hôte valide, le double tiret est commun pour IDN punycode. Le numéro de port doit être supprimé. C'est le nettoyage du code.
import re
def is_valid_hostname(hostname):
if len(hostname) > 255:
return False
hostname = hostname.rstrip(".")
allowed = re.compile("(?!-)[A-Z\d\-\_]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
# convert your unicode hostname to punycode (python 3 )
# Remove the port number from hostname
normalise_Host = hostname.encode("idna").decode().split(":")[0]
is_valid_hostanme(normalise_Host )
J'aime la minutie de la réponse de Tim Pietzcker, mais je préfère décharger une partie de la logique des expressions régulières pour des raisons de lisibilité. Honnêtement, je devais rechercher la signification de ces parties (?
"extension notation". De plus, j’estime que l’approche «double négatif» est plus évidente en ce sens qu’elle limite la responsabilité de l’expression régulière à la simple recherche de tout caractère non valide. J'aime bien que re.IGNORECASE permette de raccourcir la regex.
Alors, voici un autre coup; c'est plus long mais ça se lit un peu comme de la prose. Je suppose que "lisible" est un peu en contradiction avec "concis". Je crois que toutes les contraintes de validation mentionnées dans le fil jusqu'à présent sont couvertes:
def isValidHostname(hostname):
if len(hostname) > 255:
return False
if hostname.endswith("."): # A single trailing dot is legal
hostname = hostname[:-1] # strip exactly one dot from the right, if present
disallowed = re.compile("[^A-Z\d-]", re.IGNORECASE)
return all( # Split by labels and verify individually
(label and len(label) <= 63 # length is within proper range
and not label.startswith("-") and not label.endswith("-") # no bordering hyphens
and not disallowed.search(label)) # contains only legal characters
for label in hostname.split("."))
Cette expression rationnelle pure doit respecter tous les paramètres:
^(?=.{1,253}\.?$)(?!-)[A-Za-z0-9\-]{1,63}(\.[A-Za-z0-9\-]{1,63})*\.?(?<!-)$
def is_valid_Host(host):
'''IDN compatible domain validator'''
Host = Host.encode('idna').lower()
if not hasattr(is_valid_Host, '_re'):
import re
is_valid_Host._re = re.compile(r'^([0-9a-z][-\w]*[0-9a-z]\.)+[a-z0-9\-]{2,15}$')
return bool(is_valid_Host._re.match(Host))