如何使用正则表达式和PHP验证域名?

23

我希望有一种方法可以验证域名而不是完整的URL,以下示例是我要寻找的:

example.com -> true
example.net -> true
example.org -> true
example.biz -> true
example.co.uk -> true
sub.example.com -> true
example.com/folder -> false
exam*$ple.com -> false

这个链接https://dev59.com/x0bRa4cB1Zd3GeqPxBrM有关于使用正则表达式匹配域名的更多信息。 - Gavin Mogan
7个回答

96

被接受的答案是不完整/错误的。

正则表达式模式;

  • 不应该验证诸如以下域名:
    -example.com, example--.com, -example-.-.com, example.000等...

  • 应该验证诸如以下域名:
    schools.k12, newTLD.clothing, good.photography等...

经过进一步研究,以下是我能想到的最正确、跨语言和紧凑的模式:

^(?!\-)(?:(?:[a-zA-Z\d][a-zA-Z\d\-]{0,61})?[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}$

这个模式符合规范中大部分的规则,包括:
  • 每个标签/级别(由点分隔)最多可以包含63个字符
  • 完整的域名最多可以有127个级别
  • 完整的域名在文本表示中不能超过253个字符的长度。
  • 每个标签可以由字母、数字和连字符组成。
  • 标签不能以连字符开头或结尾
  • 顶级域名(扩展名)不能是全数字的。

注1:完整域名长度检查不包括在正则表达式中。应该通过本地方法进行简单检查,例如strlen(domain) <= 253
注2:此模式适用于大多数语言,包括PHP、Javascript、Python等...

请参见此处的演示(适用于JS、PHP、Python)

更多信息:

  • 上述正则表达式不支持IDN

  • 没有规范说明扩展名(TLD)应该在2到6个字符之间。实际上支持63个字符。请参见当前的TLD列表 here。此外,一些网络内部使用自定义/伪造的TLD。

  • 注册机构可能会强制实施一些额外的特定规则,这些规则在此正则表达式中没有明确支持。例如,.CO.UK.ORG.UK必须至少有3个字符,但不能超过23个字符,不包括扩展名。这些规则是非标准的,可能会发生变化。如果无法维护,请勿实施这些规则。

  • 正则表达式很棒,但并不是解决每个问题的最有效、最高效的方法。因此,应尽可能使用本地URL解析器。例如,Python的urlparse()方法或PHP的parse_url()方法...

  • 毕竟,这只是格式验证。正则表达式测试并不能确认域名是否实际配置/存在!您应该通过发出请求来测试其存在性。

规格和参考:

更新(2019-12-21):修复了子域名的前导连字符。


2
不匹配此:go.xn--fiqs8s - Marinos An
@MarinosAn 这是一个国际域名扩展名。该帖明确指出正则表达式不支持国际化域名(IDN)。 - Onur Yıldırım
无法在bash中使其工作。有人能解释一下为什么吗?我知道OP要求使用PHP,但如果可能的话,我想在shell脚本中使用它。 - dimmech
由于某些原因,这会通过子域名.-域名.com。 - Saud Iqbal
1
下划线不是一个有效的字符。 - Onur Yıldırım
显示剩余6条评论

32

如何尝试:

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

6
谁曾经点了踩,现在已经撤回了。@Lauri 提到了".museum" 和 ".travel"。 - zildjohn01
4
这个答案(并不完全错误),但是不完整。请查看我回答中的更正。 - Onur Yıldırım
这个方案是可行的 - 但是现在我们即将获得一堆新的 gTLD,其中一些超过12个字符。为了未来的保障,至少应该允许20个字符。即使如此,你最终可能会遇到瓶颈。 - Sk446
长度必须保持在64个字符或更少! - Indolering
3
只有在PHP中,如果您将REGEX设置为/^(?:[-A-Za-z0-9]+\.)+[A-Za-z]{2,6}$/,它才能正常工作。例如,它可以与preg_match函数一起使用,否则无法正常工作。 - Villapalos
显示剩余4条评论

4
请尝试使用以下表达式:
^(http[s]?\:\/\/)?((\w+)\.)?(([\w-]+)?)(\.[\w-]+){1,2}$

它的实际作用

  • 可选的http/s://
  • 可选的www
  • 任何有效的字母数字名称(包括-_
  • 1或2个出现的任何有效的字母数字名称(包括-_

验证示例

  • http://www.test.example
  • test.com.mt

这个例子是完美的,涵盖了http(s)://www.abc.com、www.abc.com、abc.com和subdomain.abc.com子域名。 - Rakesh Kumar

2
在我的情况下,如果域名格式为stackoverflow.comxxx.stackoverflow.com,则被视为有效。因此,除了其他堆栈答案之外,我还添加了对www.的检查。
function isValidDomainName($domain) {
  if (filter_var(gethostbyname($domain), FILTER_VALIDATE_IP)) {
      return (preg_match('/^www./', $domain)) ? FALSE : TRUE;
  }
  return FALSE;
}

你可以使用这段代码测试该函数

    $domain = array("http://www.domain.example","http://www.domain.example/folder" ,"http://domain.example", "www.domain.example", "domain.example/subfolder", "domain.example","sub.domain.example");
    foreach ($domain as $v) {
        echo isValidDomainName($v) ? "{$v} is valid<br>" : "{$v} is invalid<br>";
    }

0
我创建了一个函数来验证域名,而不需要使用任何正则表达式。
<?php
function validDomain($domain) {
  $domain = rtrim($domain, '.');
  if (!mb_stripos($domain, '.')) {
    return false;
  }
  $domain = explode('.', $domain);
  $allowedChars = array('-');
  $extenion = array_pop($domain);
  foreach ($domain as $value) {
    $fc = mb_substr($value, 0, 1);
    $lc = mb_substr($value, -1);
    if (
      hash_equals($value, '')
      || in_array($fc, $allowedChars)
      || in_array($lc, $allowedChars)
    ) {
      return false;
    }
    if (!ctype_alnum(str_replace($allowedChars, '', $value))) {
      return false;
    }
  }
  if (
    !ctype_alnum(str_replace($allowedChars, '', $extenion))
    || hash_equals($extenion, '')
  ) {
    return false;
  }
  return true;
}
$testCases = array(
  'a',
  '0',
  'a.b',
  'google.com',
  'news.google.co.uk',
  'xn--fsqu00a.xn--0zwm56d',
  'google.com ',
  'google.com.',
  'goo gle.com',
  'a.',
  'hey.hey',
  'google-.com',
  '-nj--9*.vom',
  ' ',
  '..',
  'google..com',
  'www.google.com',
  'www.google.com/some/path/to/dir/'
);
foreach ($testCases as $testCase) {
  var_dump($testCase);
  var_dump(validDomain($TestCase));
  echo '<br /><br />';
}
?>

这段代码输出:

字符串(1) "a" 布尔(false)
字符串(1) "0" 布尔(false)
字符串(3) "a.b" 布尔(true)
字符串(10) "google.com" 布尔(true)
字符串(17) "news.google.co.uk" 布尔(true)
字符串(23) "xn--fsqu00a.xn--0zwm56d" 布尔(true)
字符串(11) "google.com " 布尔(false)
字符串(11) "google.com." 布尔(true)
字符串(11) "goo gle.com" 布尔(false)
字符串(2) "a." 布尔(false)
字符串(7) "hey.hey" 布尔(true)
字符串(11) "google-.com" 布尔(false)
字符串(11) "-nj--9*.vom" 布尔(false)
字符串(1) " " 布尔(false)
字符串(2) ".." 布尔(false)
字符串(11) "google..com" 布尔(false)
字符串(14) "www.google.com" 布尔(true)
字符串(32) "www.google.com/some/path/to/dir/" 布尔(false)

我希望我已经涵盖了所有内容,如果我漏掉了什么,请告诉我,我可以改进这个函数。:)


0

之前我曾尝试寻找解决方案,但考虑到子域名的可能值,我可能错误地认为唯一确定某个内容是否有效的方法是检查这个数组(可以像ICANN网站示例中那样提取)。

http://www.axew3.com/www/data-hints/w3-all-top-level-domains-names-array.php

就像这样:

// this extract ever the correct cookie domain (except for sub hosted/domains like: mydomain.my-hostingService-domain.com)

function extract_cookie_domain( $w3cookie_domain ) {

require_once( WPW3ALL_PLUGIN_DIR . 'addons/w3_icann_domains.php' );

$count_dot = substr_count($w3cookie_domain, ".");

     if($count_dot >= 3){
      preg_match('/.*(\.)([-a-z0-9]+)(\.[-a-z0-9]+)(\.[a-z]+)/', $w3cookie_domain, $w3m0, PREG_OFFSET_CAPTURE);
      $w3cookie_domain = $w3m0[2][0].$w3m0[3][0].$w3m0[4][0];
   }
   
   $ckcd = explode('.',$w3cookie_domain);
// $w3all_domains array come from file inclusion where icann domains are stored- This is the unique way to check if a domain is valid and to complete any answer, or any answer, will be incomplete
  if(!in_array('.'.$ckcd[1], $w3all_domains)){
   $w3cookie_domain = preg_replace('/^[^\.]*\.([^\.]*)\.(.*)$/', '\1.\2', $w3cookie_domain);
  }

    $w3cookie_domain = '.' . $w3cookie_domain;

$pos = strpos($w3cookie_domain, '.');
if($pos != 0){
    $w3cookie_domain = '.' . $w3cookie_domain;
}

return $w3cookie_domain;

}

但也许我错了。你怎么看?

附言:我没有重新检查函数的逻辑,也许它可以缩短并且肯定可以改进。

$w3all_domains数组来自文件包含,其中存储了ICANN域-这是检查域是否有效并完成任何答案的唯一方法:或者我认为任何上面或下面的解决方案都有时不完整。

[编辑]


这个问题要求进行“验证”,但是你的回答似乎展示了一个“提取”过程。你是否在回答所问的问题? - mickmackusa
好的,这是获得正确结果的唯一方法,否则你就永远不会确定,如果之前没有像这样经历过。不是吗? - axew3

0

记住,正则表达式只能检查某些东西是否格式正确。www.idonotexistbecauseiammadeuponthespot.example是格式正确的,但实际上并不存在...在写作时。;) 此外,某些免费的Web托管提供商(如Tripod)允许子域中使用下划线。这显然违反了RFC,但有时确实有效。

您想检查域名是否存在吗?请尝试使用dns_get_record而不是(仅仅)正则表达式。


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