regex-tuesday,每周二会出一道正则表达式的题目,本周的题目比较简单,如果你答出来了,可以留言,如果答不出来.可以看看我的答案和解释.下面内嵌了原题目页面.你要做的就是使用JavaScript正则字面量的写法/regex/i,来通过所有的测试用例.
查看答案
这个题目主要是让我们写一个匹配标准域名的正则.首先要做的是:观察用例.
1.协议
从用例中看出,只有两种协议是合法的,http和https,各种伪协议都是不能通过的.于是我们的正则可以写成这样:
/^https?:\/\// //匹配情况27/41
2.最后一级域名
通过观察,可以看到最后一级域名中允许使用的字符有字母和数字,还有连字符,但连字符左右两边必须至少有一个字母或数字.也就可以理解成,左右两边的字符是[a-z0-9],中间的字符可以是多个[a-z0-9-],由于域名本身就是忽略大小写的,所以这里需要加上正则选项i,于是我们的正则成了这样:
/^https?:\/\/[a-z0-9][a-z0-9-]*?[a-z0-9]/i //匹配情况30/41
但是发现http://a.b这个用例通不过了.因为它只有a这一个字符.那么我们需要把中间的字符和最右边的字符放在一个可选的非捕获分组中:
/^https?:\/\/[a-z0-9](?:[a-z0-9-]*?[a-z0-9])?/i //匹配情况31/41
3.倒数第二级到顶级域名
剩下的部分就有一定规则了.都是点后面跟几个字符.xxxx这样的结构.其中字符的要求和上面最后一级域名中的一样.于是我们有了这样的正则:
/^https?:\/\/[a-z0-9](?:[a-z0-9-]*?[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*?[a-z0-9])?)+/i //匹配情况36/41
4.尾部可选的斜杠
这是收尾工作,加上可选的斜杠和$.
/^https?:\/\/[a-z0-9](?:[a-z0-9-]*?[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*?[a-z0-9])?)+\/?$/i //匹配情况39/41
5.长度限制
这时发现最后两个用例仍没通过,如果了解一些规范的话,会知道每一级域名包含的字符不能超过63个,完整的域名字符数不能超过255个.这些都规定在rfc1035中.
首先限制第一个,每一级域名字符数不超过63个,通过计算,我们把两个*号都替换成{0,61},如下:
/^https?:\/\/[a-z0-9](?:[a-z0-9-]{0,61}?[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}?[a-z0-9])?)+\/?$/i //匹配情况40/41
还剩最后一个用例没有通过.怎么办呢?
这个我真没有办法,我想也无法用单纯的正则限制域名的长度.不过如果仅仅是为了通过这个用例,可以把最后的+号改成{1,100},这个100是没任何意义的.
/^https?:\/\/[a-z0-9](?:[a-z0-9-]{0,61}?[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}?[a-z0-9])?){1,100}\/?$/i //匹配情况41/41
如果真要是写在程序中,应该配合捕获分组以及保存该分组数据的$1属性:
if (/^https?:\/\/([a-z0-9](?:[a-z0-9-]{0,61}?[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}?[a-z0-9])?)+)\/?$/i.test(value) && RegExp.$1.length <= 255) {
//value是个合法的域名
}