什么是匹配有效的无子域名的正则表达式?

171

我需要验证域名:

google.com

stackoverflow.com

因此,这是一个最原始的域名形式 - 甚至没有像www这样的子域名。

  1. 字符应该只有a-z | A-Z | 0-9句点(.) 和 破折号(-)
  2. 域名部分不应以破折号(-)开头或结尾 (例如-google-.com)
  3. 域名部分长度应在1到63个字符之间
  4. 扩展名(TLD)现在可以是规则#1下的任何内容,但稍后可能会根据列表对它们进行验证,但它应该至少是1个字符

编辑:目前TLD似乎为2-6个字符

第4点修改:TLD实际上应标记为“子域名”,因为它应包括像.co.uk这样的内容 - 我认为除了检查列表外,可能唯一的验证方式是“在第一个点之后,应存在符合规则#1的一个或多个字符”

非常感谢,相信我我确实尝试过!


1
也许完全没有帮助。当涉及到google.co.uk和一些日本域名时,我相信您在使用正则表达式之前必须三思而后行。我的个人想法是,正则表达式不足以将域名验证为真实的域名。 顺便说一下,这里有一个几乎完整的顶级域名和国家代码二级域名列表:http://static.ayesh.me/misc/SO/tlds.txt - AKS
2
请查看我在关于主机名验证的相关问题中的答案。 - SAM
2
常常被忽略的是:对于完整的域名,您应该在顶级域后匹配一个句点。 - schmijos
1
已经过去了4年,现在计数达到了89,000。 - mydoglixu
2
这些答案中有一些相当不错,但是也有另一个问题的好答案也值得一看。 - craftworkgames
显示剩余5条评论
23个回答

135

我知道这是一个有点老的帖子,但是所有的正则表达式都缺少一个非常重要的组件:支持IDN域名。

IDN域名 以xn--开头。它们允许在域名中使用扩展的UTF-8字符。例如,你知道“♡.com”是一个有效的域名吗?是的,“爱心点com”!为了验证域名,你需要让http://xn--c6h.com/通过验证。

请注意,要使用这个正则表达式,你需要将域名转换为小写,并使用一个IDN库来确保你将域名编码为ACE(也称为“ASCII兼容编码”)。一个好的库是GNU-Libidn。

idn(1) 是国际化域名库的命令行接口。以下示例将UTF-8格式下的主机名转换为ACE编码形式。生成的URL https://nic.xn--flw351e/ 就可以用作https://nic.谷歌/的ACE编码等价物。

  $ idn --quiet -a nic.谷歌
  nic.xn--flw351e

这个神奇的正则表达式应该覆盖了 大部分 域名(虽然,我确信还有很多我没有考虑到的有效特殊情况):

^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$

在选择域验证正则表达式时,您应该查看域是否匹配以下内容:

  1. xn--stackoverflow.com
  2. stackoverflow.xn--com
  3. stackoverflow.co.uk
如果这三个域名没有通过,你的正则表达式可能不允许合法的域名!
请查看Oracle国际语言环境指南中的国际化域名支持页面以获取更多信息。
可以在这里尝试使用正则表达式:http://www.regexr.com/3abjr ICANN保留了已授权的顶级域名列表,其中可用于查看一些IDN域名的示例。

编辑:

 ^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9-]{1,30}\.[a-z]{2,})$

这个正则表达式将阻止以'-'结尾的主机名域被标记为有效。此外,它允许无限子域。


1
请注意,此代码仅支持一个子域名,多于一个子域名将导致错误。这不是您在使用内部站点等情况下会遇到的问题... 为了快速尝试支持更多的子域名,请使用以下正则表达式:/^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,}\.?((xn--)?([a-z0-9\-.]{1,61}|[a-z0-9-]{1,30})\.?[a-z]{2,})$/i - stakolee
5
任何字符或字符类或组即使没有 {1,1},也只匹配一次。这就像写 h{1,1}i{1,1} 而不是 hi 一样,只会让阅读更加困难。 - Eugene Morozov
2
@FilipBartuzi提到了这个问题:使用IDN库来确保您将域名编码为ACE。它们实际上不是有效的域名(DNS仅支持一部分字母和数字,ACE/IDN是使它们工作的方法)。 - Tim Groeneveld
1
需要注意的是,xn--stackoverflow.com 不是一个有效的名称,因为 'stackoverflow' 无法从Punycode转换。然而,这已经超出了正则表达式的能力范围。一般来说,xn--[a-z0-9]+ 标签将仅限于IDN,而 xn--[a-z0-9]+\-[a-z0-9]+ 则表示ASCII和非ASCII字符的混合。 - Marcus
2
@Sandra com.com 是一个有效的(并且已注册!)域名。因此,它的任何子域名也都是有效的。这个问答是关于验证一个域名的语法是否正确,而不是它是否适合特定的用途,或者某人是否输入了"正确"的域名。 - Walf
显示剩余19条评论

68

嗯,这其实比看起来要稍微复杂一些(请参见评论),因为它需要满足您的具体需求:

/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/

但请注意,这将拒绝许多有效的域名。


15
尽管这个正则表达式符合你的规格,但你的规格是错误的。 g.co 是一个有效的域名,但 g 只有一个字符。 - sch
3
这应该匹配所有情况:^([a-z0-9])(([a-z0-9-]{1,61})?[a-z0-9]{1})?(.a-z0-9?)?(.[a-zA-Z]{2,4})+$ - transilvlad
2
x.com 在这里无法通过。 - Neil McGuigan
5
@Neil: 你说得对。原问题要求3-63个字符(参见编辑3)。可以很容易地更改以支持单个字符域名:/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/。但是这仍然会拒绝大量有效的内容... - Cameron
2
这甚至不支持 .co.uk 域名后缀。 - stardust4891
显示剩余17条评论

55

我的正则表达式如下:

^[a-zA-Z0-9][a-zA-Z0-9-_]{0,61}[a-zA-Z0-9]{0,1}\.([a-zA-Z]{1,6}|[a-zA-Z0-9-]{1,30}\.[a-zA-Z]{2,3})$

它可以匹配 i.oh1.mewow.british-library.uk

更新:

这是更新后的规则。

^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$

正则表达式可视化

https://www.debuggex.com/r/y4Xe_hDVO11bv1DV

现在它检查域名标签的开头或结尾是否有-_


9
看起来不错,但"{2,6}"的标准需要更新以适应新顶级域名,可能改为"{2,}"。 - jwatts1980
1
这是一篇讨论即将到来的变化的文章,其中包括示例和相关资源链接:http://www.zdnet.com/ready-or-not-here-come-the-new-internet-top-level-domain-names-7000025744/ - jwatts1980
@behz4d,你能否提供一个例子? - paka
1
为什么使用([a-zA-Z]{1}[a-zA-Z]{1})而不是([a-zA-Z]{2})? - Anton
4
最后两个备选项也是错误的:存在接受IDNA子标签(两个字母)的ccTLDs。现在也存在已经使用IDNA标签的TLD标签。 您不应该将最后一个标签特殊处理,它与其他标签没有区别(现在有许多扩展名添加了可变长度,就像子域中的所有其他标签一样)。 请注意,IDNA标签也可能出现Puny编码(在这种情况下,标签中会有“--”段,这是标签中唯一允许出现“--"的情况)。 最后,在所有标签中,下划线都无效。 - verdy_p
显示剩余13条评论

45

我的赌注:

^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$

解释:

域名由多个段组成。这里是一个段(除了最后一个):

[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?

它可以有1-63个字符,不能以“-”开头或结尾。

现在在其末尾添加“.”并至少重复一次:

(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+

然后附加最后一段,长度为2-63个字符:

[a-z0-9][a-z0-9-]{0,61}[a-z0-9]

在这里进行测试:http://regexr.com/3au3g


1
所有其他答案对我都没有用,但这个有用。 - Chololoco
1
@pubkey,您需要将这些域名转换为[punycode](https://en.wikipedia.org/wiki/Punycode)。 редбулл.москва的实际名称是xn--90afc0aazy.xn--80adxhks,我的正则表达式可以匹配它。 - Yaroslav Stavnichiy
4
这确实是最佳的正则表达式,不会失控。它处理单个字符标签,可以处理IDN域名(转换为punycode),而且对TLD没有荒谬的长度要求。我认为很难找到它无法匹配的域名。它唯一没有强制执行的是域名的最大长度(253个字符)。但是,一个简单的长度检查可以很容易地与正则表达式一起使用。 - Nicholi
1
据我所知,域名不应以数字开头或结尾(根据 第8页 的 rfc1035 规定)。但请注意,我可能在以数字结尾方面是错误的;这可能是允许的,但我从未见过。我修改后的版本(仅删除了第一个和最后一个序列中的 0-9):^(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z]$ - patricknelson
1
一个完全合格的域名可以以一个最后的点结束。http://www.dns-sd.org/trailingdotsindomainnames.html - Craig McQueen
显示剩余7条评论

21

本答案适用于域名(包括服务RR),而不适用于主机名(比如电子邮件主机名)。

^(?=.{1,253}\.?$)(?:(?!-|[^.]+_)[A-Za-z0-9-_]{1,63}(?<!-)(?:\.|$)){2,}$

这基本上是mkyong的答案并且还有:

  • 最大长度为255个八位字节,包括长度前缀和空根。
  • 允许使用显式dns根节点结尾的'.'
  • 允许服务域名RRs使用前导'_' (错误: 不强制限制'_标签'的最大长度为15个字符,也不要求至少存在一个在服务RRs上面的域)
  • 匹配所有可能的TLDs。
  • 不捕获子域标签。

按部分分析

向前查看,在'^$'之间限制最大长度为253个字符,并带有可选的'.'结尾文字。

(?=.{1,253}\.?$)

向前查看,下一个字符不是“-”,且在下一个“.”之前的任何字符后面没有“_”。也就是说,强制标签的第一个字符不是“-”,并且只有第一个字符可以是“_”。

(?!-|[^.]+_)

每个标签允许1到63个字符。

[A-Za-z0-9-_]{1,63}

后顾,前一个字符不是“-”。也就是说,强制标签的最后一个字符不是“-”。

(?<!-)

每个标签末尾都要强制加上一个“.”,除了最后一个标签外,该标点为可选项。

(?:\.|$)

大多数情况下,这需要至少两个域级别,尽管并不完全正确,但通常是一个合理的假设。如果要允许顶级域名或未经认证的相对子域通过(例如localhost、myrouter、to),则将{2,}更改为+。

(?:(?!-|[^.]+_)[A-Za-z0-9-_]{1,63}(?<!-)(?:\.|$)){2,}

这个表达式的单元测试。


2
谢谢!这是最好的正则表达式。您详细的解释和单元测试是额外的奖励。 - naudster
"RR" 的意思是什么? - wheeler
资源记录。通常是一个文本或信息字段,告诉您如何与服务进行交互。 - Andrew Domaszek
这个正则表达式不正确。例如,域名redbull.移动是有效的,但是该正则表达式无法匹配。 - pubkey
先将其转换为punycode,然后进行匹配。在pre-punycode版本上实施长度限制确实很困难。 - Andrew Domaszek

17

3
注意:这个域名 www.my---domain.com 不会被认为是有效的(虽然很少见)。 - Chris Bier
18
不能用新通用顶级域名(例如.photography)来满足需求。 - Sam Figueroa
2
@SamFigueroa 你只需要修改它的长度。 - Steel Brain
3
不应该检查TLD,因为它与子域名没有区别。基于当前“可用”的TLD的正则表达式不具备未来可扩展性。 - Loïc Faure-Lacroix
1
建议将最后一位设置为 {2,63}:请参见 https://dev59.com/R2ox5IYBdhLWcg3wYzbX - Eric Dobbs
显示剩余2条评论

15

接受的答案对我无效,请尝试以下方法:

^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$

访问单元测试用例进行验证。


4
不支持新的更长的顶级域名,比如.audio、.photography等大多数域名。具体列表可参考http://data.iana.org/TLD/tlds-alpha-by-domain.txt。 - mrbinky3000
@mrbinky3000 只需将最后的 {2,6} 更改为其他内容即可正常工作。我的是:^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+(?!-)[a-zA-Z0-9-]{1,63}(?<!-)$ - Mygod
1
@MightyPork 你说得对!抱歉,这里是(希望)干净的版本:^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+(?!-)[a-zA-Z0-9-]{1,63}(?<!-)$ - Mygod
1
所以我把它改成了/^(?:(?!-)[a-z0-9-]{0,62}[a-z0-9]\.)+[a-z]{2,}$/i。虽然不太优雅,但能胜任工作。 - PhiLho
我喜欢这个:它相当简单和短小,解释得很好(即使必须转到另一页进行解释),允许潜在的未来选项(如果他们再次提出类似 xn-- 的东西)...唯一的问题是 TLD 限制,在2019年应该超过6个字符长度,也许有一天我们会有IDN TLDs,所以我只需在用户输入中添加一个点并删除TLD匹配部分(但保留“$”)。 - Luc
显示剩余4条评论

12

正如已经指出的那样,在实际意义上很难区分子域名(例如.co.uk域名)。我们使用这个正则表达式来验证在实际应用中出现的域名。它涵盖了我所知道的所有实际使用情况。欢迎新的情况。根据我们的准则, 它避免了非捕获组和贪婪匹配。

^(?!.*?_.*?)(?!(?:[\d\w]+?\.)?\-[\w\d\.\-]*?)(?![\w\d]+?\-\.(?:[\d\w\.\-]+?))(?=[\w\d])(?=[\w\d\.\-]*?\.+[\w\d\.\-]*?)(?![\w\d\.\-]{254})(?!(?:\.?[\w\d\-\.]*?[\w\d\-]{64,}\.)+?)[\w\d\.\-]+?(?<![\w\d\-\.]*?\.[\d]+?)(?<=[\w\d\-]{2,})(?<![\w\d\-]{25})$

证明、解释和示例: https://regex101.com/r/FLA9Bv/9 (注意:目前仅在Chrome中有效,因为正则表达式使用的是ECMA2018中才支持的lookbehinds)

验证域名有两种方法可供选择。

按照规范的FQDN匹配(理论定义,在实践中很少遇到):

实用/保守的FQDN匹配(实际定义,在实践中预期和支持):

  • 按照规定进行匹配,但以下为例外/添加内容
  • 有效字符:[a-zA-Z0-9.-]
  • 标签不能以连字符开头或结尾(根据RFC-952RFC-1123/2.1
  • TLD最小长度为2个字符,最大长度为24个字符,根据当前现有记录
  • 不匹配尾随点

虽然它不应该验证 a.b-.cc,但它很棒。 - Ste
你说得对,感谢指出。不能保证现在就去研究,但很乐意听取建议。 - thisismydesign
这个正则表达式会导致你的应用在Safari中崩溃。 - Filip
干得好!我为逗号分隔的域名制作了一个变体:https://dev59.com/uofca4cB1Zd3GeqPeBf2#73896662 - Gawrion

9
^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,7}$

领域名称应仅包含小写字母和数字,可包含连字符,并以顶级域名(TLD)结尾。 TLD 应为小写字母,长度必须介于 2 到 7 个字符之间。
http://rubular.com/ 是用于测试正则表达式的绝佳工具!
编辑:根据 Dan Caddigan 的指出,'.rentals' 的 TLD 最大长度更新为 7 个字符。

3
为什么要限制顶级域名?比如.photography现在就变成无效了。可以把字符限制取消或者其他什么方式来解决这个问题。 - adriaan

9

感谢在其他答案中指出域名验证解决方案的正确方向。域名可以通过多种方式进行验证。

如果您需要验证IDN域名的可读形式,则可以使用正则表达式\p{L}。这将匹配任何语言中的任何字符。

请注意,最后一部分可能也包含连字符!因为punycode编码的中文名称可能在tld中有Unicode字符。

我找到了解决方案,例如:

  • google.com
  • masełkowski.pl
  • maselkowski.pl
  • m.maselkowski.pl
  • www.masełkowski.pl.com
  • xn--masekowski-d0b.pl
  • 中国互联网络信息中心.中国
  • xn--fiqa61au8b7zsevnm8ak20mc4a87e.xn--fiqs8s

正则表达式如下:

^[0-9\p{L}][0-9\p{L}-\.]{1,61}[0-9\p{L}]\.[0-9\p{L}][\p{L}-]*[0-9\p{L}]+$

点击此处进行检查和调整

注意:该正则表达式相当宽容,因为当前域名允许的字符集也很宽泛。

更新:进一步简化,由于 a-aA-Z\p{L}\p{L} 相同。

注意2:唯一的问题是它会匹配包含双点的域名,比如 masełk..owski.pl。如果有人知道如何解决这个问题,请加以改进。


我们可以直接使用[:alpha:][:digit]代替\p{L}。这样也能正常工作。 - puchu
您不能在将国际化域名(IDN)转换为punycode之前使用此方法进行验证。例如,使用您的表达式,中国互联网络信息中心中国互联网络信息中心中国互联网络信.中国 被检查为有效,但在IDN转换后,每个标签的字节数过多。\p{L}匹配符号,而不是punycode字节(这些字节从符号到符号不同),因此当尝试限制其转换后的大小时,重复计数是无用的。 - Andrew Domaszek
好的,每个部分都限制在64字节以内。然而我们无法使用RegExp进行检查,因此需要使用punycode解码器进行进一步的验证步骤——这将在您的示例主机名上失败。中国人一定会对这种限制感到疯狂。 - PeterM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接