子域名的正则表达式

44

有人知道如何编写一个正则表达式,只允许使用 a-zA-Z0-9.-(字母、数字、点和短横线),但不能以点或短横线开头或结尾吗?

我尝试了这个:

/^[^.-][a-zA-Z0-9.-]+[^.-]$/

...但是如果我写类似"john@"这样的内容,它会生效,而我不想这样做,因为@是不允许的。


1
正则表达式使用哪种语言?(Perl,Egrep,Awk,Vim,JavaScript...) - Benoit
11个回答

108

子域名

根据相关的互联网建议(RFC3986第2.2节,该节参考了RFC1034第3.5节RFC1123第2.1节),子域名(DNS域名主机名的一部分)必须满足以下几个要求:

  • 每个子域名部分的长度不得超过63个字符。
  • 每个子域名部分必须以字母数字(即字母[A-Za-z]或数字[0-9])开头和结尾。
  • 每个子域名部分可以包含连字符,但不能以连字符开头或结尾。

以下是一个满足这些要求的子域名部分的表达式片段:

[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?

请注意,此表达式片段不能单独使用 - 它需要在更大的上下文中结合边界条件,如下所示表达式验证DNS主机名...

DNS主机名

命名的主机(而不是IP地址)必须满足以下附加要求:

  • 主机名可以由多个子域名部分组成,每个子域名部分用单个点隔开。
  • 整个主机名的长度不应超过255个字符。
  • 顶级域(DNS主机名最右侧的部分)必须是国际上公认的值之一。有效顶级域的列表由IANA.ORG维护。 (在此处查看裸骨当前列表:http://data.iana.org/TLD/tlds-alpha-by-domain.txt)。

因此,以下是一个有注释的正则表达式(使用PHP语法),可以伪验证DNS主机名:(请注意,这里结合了上面的子域名部分的修改版本,并对其进行了注释)。

更新时间:2016年8月20日:由于本回答最初发布于2011年,顶级域名数量已经激增。截至2016年8月,现在已有超过1400个顶级域名。原本回答中的正则表达式包含了所有这些内容,但这已不再实际可行。下面的新正则表达式包含了不同的顶级域名表达式。该算法来自:顶级域名规范草案-liman-tld-names-06

$DNS_named_host = '%(?#!php/i DNS_named_host Rev:20160820_0800)
    # Match DNS named host domain having one or more subdomains.
    # See: https://dev59.com/BWsz5IYBdhLWcg3wZm3Q#7933253
    ^                     # Anchor to start of string.
    (?!.{256})            # Whole domain must be 255 or less.
    (?:                   # One or more sub-domains.
      [a-z0-9]            # Subdomain begins with alpha-num.
      (?:                 # Optionally more than one char.
        [a-z0-9-]{0,61}   # Middle part may have dashes.
        [a-z0-9]          # Starts and ends with alpha-num.
      )?                  # Subdomain length from 1 to 63.
      \.                  # Required dot separates subdomains.
    )+                    # End one or more sub-domains.
    (?:                   # Top level domain (length from 1 to 63).
      [a-z]{1,63}         # Either traditional-tld-label = 1*63(ALPHA).
    | xn--[a-z0-9]{1,59}  # Or an idn-label = Restricted-A-Label.
    )                     # End top level domain.
    $                     # Anchor to end of string.
    %xi';  // End $DNS_named_host.

请注意,这个表达式并不完美。它需要一个或多个子域名,但从技术上讲,主机可以由没有子域的顶级域组成(但这很少见)。
2014-08-12更新:添加了不需要交替的子域的简化表达式。
2016-08-20更新:修改了DNS主机名正则表达式,以便(更普遍地)匹配大量新的有效顶级域。此外,还从答案中删除了不必要的材料。

1
嗯,我认为双“--”也不是有效的,但使用这个正则表达式可能是可行的,对吧? - algorhythm
3
根据RFC规定,使用双横线是完全有效的,但每个子域名部分不能以横线开头或结尾。 - ridgerunner
请注意,截至2016年,允许的顶级域名(TLD)数量比提供的DNS主机名正则表达式所允许的要多得多。 - Qqwy
@Qqwy - 是的,你说得完全正确。我有时间的时候会更新答案以反映这一点。感谢您的评论! - ridgerunner
2
这是一个很好的初步验证,但是1. 下划线是完全合法的,所以对于子域部分,^\w(?:[\w-]{0,61}\w)?$非常有效,事实上srv记录要求它们以避免与普通子域冲突 2. fyi双破折号是punycode所必需的。当然,您可以将这些验证限制为某些记录类型,但是您将不得不编写一个小解析器之类的东西,这也将允许您检查当前tld列表是否匹配 :) - sg3s
显示剩余3条评论

15

您希望将首尾字符限制为字母数字。您当前的设置允许首尾字符除了点和破折号以外的任何字符。以下设置符合您的需求:

/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/

7
可能应该允许下划线(_)符号。还有一个小提示:这个正则表达式可以简化为/^\w[\w.-]+\w$/i - RReverser
1
对于PHP。感谢您的帮助,这个代码完美运行:[a-zA-Z0-9][a-zA-Z0-9-.]+[a-zA-Z0-9] - user1018527
1
在 test.subdomain..com 上它失败了。 - Dinesh Patra
这个正则表达式无法匹配1和2个字符的子域名:a.domain.com,ab.domain.com。 - Ricardo Yubal

5
这里是一个可能对其他人有帮助的域名 + 子域名解决方案:

   /^([a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?\.)?([a-zA-Z0-9]{1,2}([-a-zA-Z0-9]{0,252}[a-zA-Z0-9])?)\.([a-zA-Z]{2,63})$/

需要通过以下chai测试:

const expect = require('chai').expect;

function testDomainValidNamesRegExp(val) {
    let names = /^([a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?\.)?([a-zA-Z0-9]([-a-zA-Z0-9]{0,252}[a-zA-Z0-9])?)\.([a-zA-Z]{2,63})$/;
    return names.test(val);
} 

let validDomainNames = [
    "example.com",
    "try.direct",
    "my-example.com",
    "subdomain.example.com",
    "example.com",
    "example23.com",
    "regexp-1222.org",
    "read-book.net",
    "org.host.org",
    "org.host.org",
    "velmart.shop-products.md",
    "ip2email.terronosp-222.lb",
    "stack.com",
    "sta-ck.com",
    "sta---ck.com",
    "9sta--ck.com",
    "sta--ck9.com",
    "stack99.com",
    "99stack.com",
    "sta99ck.com",
    "sub.do.com",
    "ss.sss-ss.ss",
    "s.sss-ss.ss",
    "s.s-s.ss",
    "test.t.te"
    ];

let invalidDomainNames = [
     "example2.com222",
     "@example.ru:?",
     "example22:89",
     "@jefe@dd.ru@22-",
     "example.net?1222",
     "example.com:8080:",
     ".example.com:8080:",
     "---test.com",
     "$dollars$.gb",
     "sell-.me",
     "open22.the-door@koll.ru",
     "mem-.wer().or%:222",
     "pop().addjocker.lon",
     "regular-l=.heroes?",
     " ecmas cript-8.org ",
     "example.com::%",
     "example:8080",
     "example",
     "examaple.com:*",
    "-test.test.com",
    "-test.com",
    "dd-.test.com",
    "dfgdfg.dfgdf33.e",
    "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd-.test.com",
    "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd.testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt.com",
    "d-.test.com"
];

describe("Test Domain Valid Names RegExp", () => {
    validDomainNames.forEach((val) => {
        it(`Text: ${val}`, () => {
            expect(testDomainValidNamesRegExp(val)).to.be.true;
        });
    });
});

describe("Test Domain Invalid Names RegExp", () => {
    invalidDomainNames.forEach((val) => {
        it(`Text: ${val}`, () => {
            expect(testDomainValidNamesRegExp(val)).to.be.false;
        });
    });
});

非常欢迎更多的测试!


这对我非常有效。FYI,我在末尾添加了以下内容,以便它可以捕获端口号(如果存在的话,这是我的要求,因为我们在本地使用它们):(:[0-9]{0,4})? - TPHughes
如何仅验证子域名。例如,只输入文本作为子域名。 - Ameer Hamza

4
在我们的项目中,我们会像这样匹配子域名:
客户端 JS
^([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])?){2,})$

服务器 Ruby

\A([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])?){2,})\z

1

试试这个:

/^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$/

但是字符串至少要有2个字符才能匹配:a a-zA-Z0-9和a a-zA-Z0-9。为了避免这种情况,您可以使用以下正则表达式:

/^[a-zA-Z0-9][a-zA-Z0-9.-]*$/

但是你必须进行额外的检查,以确保字符串的结尾既不是点也不是破折号。


1

这里是用于子域名的正则表达式,

  • 允许点(.),下划线(_),破折号(-)出现在字符串中
  • 不允许点(.),下划线(_),破折号(-)出现在字符串的首尾字符中
  • 允许字符串中包含字母和数字

    ^[a-zA-Z0-9]+[a-zA-Z0-9-._]*[a-zA-Z0-9]+$

正确的示例

  • abc.com
  • abc_xyz.com
  • abc.xyz.com
  • abc

错误的例子

  • abc.
  • -abc
  • abc-
  • xyz.abc-
  • https://abcxyz.com

1
尝试使用这个正则表达式:/^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$/。你代码的问题在于起始和结束的[^.-]匹配除了'.'或'-'之外的任何字符,这将匹配所有字符而不仅仅是[a-zA-Z0-9]

0
尝试一下这个正则表达式:
^(?![-.])[a-zA-Z0-9.-]+(?<![-.])$

0

如果您想针对子域名尝试,请使用以下方法:

(^[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 到无限个字母或数字字符,至少出现一次。

([.][a-zA-Z0-9]+)*

可选:一个点后面跟着0个或多个字母数字字符。

([-]+[a-zA-Z0-9]+)*

可选:一个或多个“-”后面跟着0个或多个字母数字字符。

([_]+[a-zA-Z0-9]+)*

可选:一个或多个“_”后面跟着0个或多个字母数字字符。


0
如果你想要使用破折号而没有子域名中的点,可以尝试这个正则表达式: /^\w[\w-]+\w$/

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