【发布时间】:2011-02-01 16:31:18
【问题描述】:
跟进Regular expression to match hostname or IP Address? 并使用Restrictions on valid host names 作为参考,在 Python 中匹配/验证主机名/fqdn(完全限定域名)的最易读、最简洁的方法是什么?我已经在下面的尝试中回答了,欢迎改进。
【问题讨论】:
标签: python regex validation hostname fqdn
跟进Regular expression to match hostname or IP Address? 并使用Restrictions on valid host names 作为参考,在 Python 中匹配/验证主机名/fqdn(完全限定域名)的最易读、最简洁的方法是什么?我已经在下面的尝试中回答了,欢迎改进。
【问题讨论】:
标签: python regex validation hostname fqdn
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("."))
确保每个细分市场
它还避免了双重否定(not disallowed),如果hostname 以. 结尾,那也没关系。如果hostname 以多个点结尾,它将(并且应该)失败。
【讨论】:
re.match - 请注意re.match("a+", "ab") 是匹配项,而re.match("a+$", "ab") 不是。您的函数也不允许在主机名末尾有一个点。
re.match 需要匹配整个字符串,因此不需要字符串结尾的锚点。但正如我现在发现的(谢谢!)它只将匹配绑定到字符串的开头。我相应地更正了我的正则表达式。但是,我不明白你的第二点。以点结尾的主机名是否合法?问题中链接的*文章似乎说不。
不要重新发明*。您可以使用库,例如验证器。或者你可以复制their code:
pip install validators
import validators
if validators.domain('example.com')
print('this domain is valid')
【讨论】:
validators.domain('localhost') 失败。
这里是Tim Pietzcker's answer 的更严格版本,具有以下改进:
[0-9] 而不是 \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)
【讨论】:
if re.match(r"\.(\d+)$", hostname):
根据The Old New Thing,DNS 名称的最大长度为 253 个字符。 (一个最多允许 255 个八位字节,但其中 2 个被编码消耗。)
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('.'))
根据自己的目的,可以争论是否接受空域名。
【讨论】:
我喜欢 Tim Pietzcker 的回答的彻底性,但我更喜欢从正则表达式中卸载一些逻辑以提高可读性。老实说,我不得不查找那些(?“扩展符号”部分的含义。此外,我觉得“双重否定”方法更明显,因为它将正则表达式的职责限制为仅查找任何无效字符。我确实喜欢 re.IGNORECASE 允许缩短正则表达式。
所以这是另一个镜头;它更长,但读起来有点像散文。我想“可读”与“简洁”有些不一致。我相信到目前为止线程中提到的所有验证约束都已涵盖:
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("."))
【讨论】:
True。
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))
【讨论】:
re.compile(),除非你使用了很多不同的模式。我同意@kaleissin 的观点,即这段代码相当模糊。只需将编译后的表达式插入到与is_valid_host() 相同范围内的私有变量中即可。您仍然只能在第一次调用时计算它并将其保存到变量中。
对@TimPietzcker 答案的补充。 Underscore 是有效的主机名字符(但不适用于域名)。而双破折号通常用于 IDN punycode 域(例如 xn--)。端口号应该被剥离。这是代码的清理。
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_hostname(normalise_host )
【讨论】:
encode("idna")。如果不是因为:,我会投票赞成 - 最初的问题没有提到任何相关内容。
我认为这个正则表达式可能对 Python 有所帮助: '^([a-zA-Z0-9]+(\.|\-))*[a-zA-Z0-9]+$'
【讨论】:
通过排除无效字符并确保非零长度来单独处理每个 DNS 标签。
def isValidHostname(hostname):
disallowed = re.compile("[^a-zA-Z\d\-]")
return all(map(lambda x: len(x) and not disallowed.search(x), hostname.split(".")))
【讨论】:
return all(x and not disallowed.search(x) for x in hostname.split("."))
. 是有效的。哦,当然,如果你想支持 IDN,还有很多工作要做......
如果您要验证现有主机的名称,最好的方法是尝试解析它。您永远不会编写正则表达式来提供这种级别的验证。
【讨论】: