一个好的正则表达式用于匹配URL是什么?

733

目前我有一个输入框,它会检测URL并解析数据。

现在我正在使用以下内容:

var urlR = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)
           (?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
var url= content.match(urlR);

问题是,当我输入像www.google.com这样的URL时,它无法工作。但是当我输入http://www.google.com时,它可以工作。

我不太精通正则表达式。有人能帮帮我吗?


2
John Gruber的自由、准确的正则表达式模式匹配URL也很好。请参考这个SO问题,了解如何修改它以在Javascript中使用。 - paleozogt
4
好的,我会尽力进行翻译。以下是需要翻译的内容:See https://mathiasbynens.be/demo/url-regex - Martin Thoma
8
(www|http:|https:)+[^\s]+[\w] - jose920405
3
这就可以了。https://regex101.com/r/S2CbwM/1 - Mukul Jain
1
网址不以“www”开头... - Greg Wozniak
显示剩余8条评论
5个回答

1035

如果您想确保URL以HTTP / HTTPS开头,请使用正则表达式:

https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)

如果您不需要HTTP协议:
[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)

要尝试此功能,请查看 http://regexr.com?37i6s,或者查看一个限制较少的版本http://regexr.com/3e6m0
JavaScript 实现示例:

var expression = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi;
var regex = new RegExp(expression);
var t = 'www.google.com';

if (t.match(regex)) {
  alert("Successful match");
} else {
  alert("No match");
}


26
忘了提到,请使用网站http://gskinner.com/RegExr/来测试正则表达式并查看常见示例。 - Daveo
10
这仍然匹配没有有效顶级域名的URL,例如:"http://foo/file.html"。 - Jesse Fulton
12
regex.test('//.com') => true 翻译为:正则表达式匹配 '//.com',结果为真。 - Derek Prior
23
在正则表达式中,为什么在最后一个字符类中有两条斜杠?在这个正则表达式的一部分中:[-a-zA-Z0-9@:%_+.~#?&//=]有两条斜杠,我觉得这似乎是不必要的。您在字符类中放置了两个相同的字符,如果您想转义普通斜杠,那么这将是徒劳无功的,因为转义是使用反斜杠执行的。 - Daniel Cairol
8
如果URL中有空格,则无法正常工作。 t = 'www.google.com withspace' t.match(regex) // 返回 true - Imamudin Naseem
显示剩余54条评论

404
(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})

将匹配以下情况

  • http://www.foufos.gr
  • https://www.foufos.gr
  • http://foufos.gr
  • http://www.foufos.gr/kino
  • http://werer.gr
  • www.foufos.gr
  • www.mp3.com
  • www.t.co
  • http://t.co
  • http://www.t.co
  • https://www.t.co
  • www.aa.com
  • http://aa.com
  • http://www.aa.com
  • https://www.aa.com
  • badurlnotvalid://www.google.com - captured url www.google.com
  • htpp://www.google.com - captured url www.google.com

将不会匹配以下内容

  • www.foufos
  • www.foufos-.gr
  • www.-foufos.gr
  • foufos.gr
  • http://www.foufos
  • http://foufos
  • www.mp3#.com

var expression = /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi;
var regex = new RegExp(expression);

var check = [
  'http://www.foufos.gr',
  'https://www.foufos.gr',
  'http://foufos.gr',
  'http://www.foufos.gr/kino',
  'http://werer.gr',
  'www.foufos.gr',
  'www.mp3.com',
  'www.t.co',
  'http://t.co',
  'http://www.t.co',
  'https://www.t.co',
  'www.aa.com',
  'http://aa.com',
  'http://www.aa.com',
  'https://www.aa.com',
  'badurlnotvalid://www.google.com',
  'htpp://www.google.com',
  'www.foufos',
  'www.foufos-.gr',
  'www.-foufos.gr',
  'foufos.gr',
  'http://www.foufos',
  'http://foufos',
  'www.mp3#.com'
];

check.forEach(function(entry) {
  let match = entry.match(regex);
  if (match) {
    $("#output").append( "<div style='float:left'>Success: " + entry + "</div><div style='float:right'>Captured url: " + match + "</div><br>" );
  } else {
    $("#output").append( "<div style='float:left'>Fail: " + entry + "</div><br>" );
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="output"></div>

在rubular的最新版本中检查它

在rubular的旧版本中检查它

在rubular的旧版本中检查它


17
由于新的自定义通用顶级域名可以拥有像 https://calendar.google/ 这样的 URL,所以此正则表达式已不再有效。 - Vinicius Tavares
10
但它将匹配 http://www.foufos,但不会匹配 regex.com - Qiang
27
谁还在关心特殊的www子域名?没人! - Lothar
3
如果添加了HTTP或HTTPS前缀,它将匹配,因此http://docs.google.com将匹配,但docs.google.com将不匹配。 - foufos
1
@foufos 这为什么是一个权衡?肯定有可能编写一个表达式,接受 www.google.com/test.html 但不接受 www.google..com 吧? - Rudey
显示剩余33条评论

66
这些就是你正在寻找的机器人。这段话摘自于应该真正使用的库——validator.js,也就是说如果你想自己动手的话,谁又能阻止呢?如果你只需要纯粹的正则表达式,那么你可以将长度检查去掉。不过,我认为如果你真的想确定URL的规范性,测试一下URL的长度是个好主意。
 function isURL(str) {
     var urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$';
     var url = new RegExp(urlRegex, 'i');
     return str.length < 2083 && url.test(str);
}

测试:

function isURL(str) {
         var urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$';
         var url = new RegExp(urlRegex, 'i');
         return str.length < 2083 && url.test(str);
    }
var check = [
  'http://www.foufos.gr',
  'https://www.foufos.gr',
  'http://foufos.gr',
  'http://www.foufos.gr/kino',
  'http://werer.gr',
  'www.foufos.gr',
  'www.mp3.com',
  'www.t.co',
  'http://t.co',
  'http://www.t.co',
  'https://www.t.co',
  'www.aa.com',
  'http://aa.com',
  'http://www.aa.com',
  'https://www.aa.com',
  'www.foufos',
  'www.foufos-.gr',
  'www.-foufos.gr',
  'foufos.gr',
  'http://www.foufos',
  'http://foufos',
  'www.mp3#.com'
];

for (let index = 0; index < check.length; index++) {
var url=check[index]
  if  (isURL(check[index]))
    console.log(`${url}         ✔`);
else{
  console.log(`${url}          ❌`);
}
  
}

结果 输入图像描述


22
值得一提的是,这可能会导致您的浏览器崩溃。请参见示例:http://jsfiddle.net/Lrnambtt/9/ - Ruben Martinez Jr.
3
@RubenMartinezJr.的评论还需要补充一点信息——在Chrome和Firefox上(Mac OS),它确实会使CPU达到最大值,但有趣的是,在Safari上却没有这个问题。 - rinogo
太好了!但是该函数对于维基百科的URL返回false:https://en.m.wikipedia.org/wiki/Euler–Lagrange_equation - Awolad Hossain

48

另一种可能的解决方案,上面的解决方案在解析查询字符串参数时对我不起作用。

var regex = new RegExp("^(http[s]?:\\/\\/(www\\.)?|ftp:\\/\\/(www\\.)?|www\\.){1}([0-9A-Za-z-\\.@:%_\+~#=]+)+((\\.[a-zA-Z]{2,3})+)(/(.)*)?(\\?(.)*)?");

if(regex.test("http://google.com")){
  alert("Successful match");
}else{
  alert("No match");
}

在这个解决方案中,可以随意修改[-0-9A-Za-z\.@:%_\+~#=以匹配域/子域名。在这个解决方案中,查询字符串参数也得到了处理。

如果您没有使用RegEx,那么请从表达式中用\\替换\

希望这可以帮助到您。

测试:

function IsUrl(url){
    var regex = new RegExp("^(http[s]?:\\/\\/(www\\.)?|ftp:\\/\\/(www\\.)?|www\\.){1}([0-9A-Za-z-\\.@:%_\+~#=]+)+((\\.[a-zA-Z]{2,3})+)(/(.)*)?(\\?(.)*)?");

if(regex.test(url)){
  console.log(`${url}         ✔`);
}else{
  console.log(`${url}          ❌`);
}}
var check = [
  'http://www.foufos.gr',
  'https://www.foufos.gr',
  'http://foufos.gr',
  'http://www.foufos.gr/kino',
  'http://werer.gr',
  'www.foufos.gr',
  'www.mp3.com',
  'www.t.co',
  'http://t.co',
  'http://www.t.co',
  'https://www.t.co',
  'www.aa.com',
  'http://aa.com',
  'http://www.aa.com',
  'https://www.aa.com',
  'www.foufos',
  'www.foufos-.gr',
  'www.-foufos.gr',
  'foufos.gr',
  'http://www.foufos',
  'http://foufos',
  'www.mp3#.com'
];
for (let index = 0; index < check.length; index++) {
    IsUrl(check[index])
}

结果

enter image description here


4
这段代码可以翻译为:var regex = /^(http[s]?:\/\/(www\.)?|ftp:\/\/(www\.)?|www\.){1}([0-9A-Za-z-\.@:%_\+~#=]+)+((\.[a-zA-Z]{2,3})+)(\/(.)*)?(\?(.)*)?/g; - Moreno
6
好的解决方案,但对于http://foo.co.uk会失败...必须设置为这个变量regex = new RegExp("^(http[s]?:\/\/(www\.)?|ftp:\/\/(www\.)?|(www\.)?){1}([0-9A-Za-z-\.@:%_+~#=]+)+((\.[a-zA-Z]{2,3})+)(/(.))?(\?(.))?"); 谢谢Amar。 - Tony
对于类似 https://www.elhhttp://www.elh 的情况会失败。虽然 @Tony 的解决方案通过了这种情况,但它在 www.elh 的情况下会失败。 - Elharony
如果我测试Hi there, https://www.atrable.com/#motivation is the motivation of making my app,这个正则表达式也会将“is the motivation of making my app”作为URL的一部分。为了解决这个问题,我稍微修改了一下:(http[s]?:\/\/(www\.)?|ftp:\/\/(www\.)?|www\.){1}([0-9A-Za-z-\.@:%_\+~#=]+)+((\.[a-zA-Z]{2,3})+)(/[^\s]*)?(\?[^\s]*)? - Shawn

4
我试着编写一些JavaScript代码来验证域名(例如google.com),如果验证成功,就启用提交按钮。我想分享一下我的代码,供那些寻求类似功能的人参考。它期望一个没有任何"http://"或"www."值的域名。该脚本使用了上面提供的简化了的正则表达式进行域名匹配,但对于假TLD并不严格。 http://jsfiddle.net/nMVDS/1/
$(function () {
  $('#whitelist_add').keyup(function () {
    if ($(this).val() == '') { //Check to see if there is any text entered
        //If there is no text within the input, disable the button
        $('.whitelistCheck').attr('disabled', 'disabled');
    } else {
        // Domain name regular expression
        var regex = new RegExp("^([0-9A-Za-z-\\.@:%_\+~#=]+)+((\\.[a-zA-Z]{2,3})+)(/(.)*)?(\\?(.)*)?");
        if (regex.test($(this).val())) {
            // Domain looks OK
            //alert("Successful match");
            $('.whitelistCheck').removeAttr('disabled');
        } else {
            // Domain is NOT OK
            //alert("No match");
            $('.whitelistCheck').attr('disabled', 'disabled');
        }
    }
  });
});

HTML表单:

<form action="domain_management.php" method="get">
    <input type="text" name="whitelist_add" id="whitelist_add" placeholder="domain.com">
    <button type="submit" class="btn btn-success whitelistCheck" disabled='disabled'>Add to Whitelist</button>
</form>

另一种子域名选择:https?:\/\/[\w+-_]+.{1}[\wñÑ+-_]+.{1}[\w+]{2,10} - undefined

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