如何正确的使用正则表达式验证网址链接

  • 内容
  • 评论
  • 相关

之前折腾了一个短网址程序,过程挺顺利的,唯一就是在验证网址这一步卡了壳,花费了整个过程的一大半时间,最终经过一番搜索、折腾和测试,才算找到一个完美的解决方案。

在短网址程序中,验证网址无疑是很重要的。且不说各种安全问题,就是一些「浑水摸鱼」的网址占据大量的短网址都非常让人头疼。

提到验证网址,我相信大多数人第一时间反应出来的都是正则表达式,的确,这很科学,但何奈自己正则太渣,平时想要匹配一段复杂的 HTML 都要反复调试半天,更何况我连网址的结构都不能完全梳理清楚,所以还是绕过它,咱能不用就别自己找不自在。

esc_url_raw()

想了想,平时的开发中,验证网址都是直接调的 WordPress 核心给的函数,所以考虑直接把 WordPress 的 esc_url_raw() 函数搬过来,省时又省力。

事实证明我实在是太天真了。复制的过程中我发现它牵连了太多其它的模块,一个模块勾搭一点,想要完全搬过来实在是太过庞大,到最后我都分不清楚哪是哪了,你到底跟几个模块有关系啊?照这样搞下去,还不如直接基于 WordPress 算了。

再想想,其实这个函数的本意是「过滤」网址,尽可能的把不合法的网站变成合法的:没有协议?咱给它添上协议;不合法字符?直接干掉它,别脏了眼;浏览器或服务器根本没法解析?我管你那个?

它只是在把网址变得不出现根本错误就没事,别让你这一个小小的网址把我整个程序都给搞乱了就行,虽然在 WordPress 的环境中,大多数情况这样就够了,但在我们的短网址程序中,这显然不够严格

filter_var()

自己的想法还没能运行一次,就已经胎死腹中了,没办法,只好去求助万能的搜索引擎大人了。找了半天,看着 Stack Overflow 上的各路大神的一个个长篇大论(其实我现在也好像在长篇大论……),最终决定选择大多数人推荐的 filter_var() 函数试试,其实我以前也听说过这个东西,不过一直没什么机会实际使用下,也可以趁现在好好研究研究。

filter_var() 是 PHP 自带的一个函数,顾名思义,用来验证(过滤)变量,印象中应该不止可以验证 URL,什么邮箱之类的也都玩得转。

如果要用它验证网址,只需要这样:

if ( filter_var( $url, FILTER_VALIDATE_URL ) !== false )
	echo '可以,这很网址。'; // 验证成功
else
	echo '这是什么鬼?'; // 验证失败,返回 FALSE。

非常的简单,不过要注意,失败时返回 FALSE 并不意味着你可以直接用 if ( !filter_var( ... ) ) 这样的形式来 草率的判定网址不合法,因为它成功时会返回过滤的内容,而不是 TRUE,如果内容是 '0' 那你怎么办?虽然我们验证网址不可能出现这种情况,但这种习惯还是不要养成的为好,我年轻的时候就曾自作聪明的修改例子代码,在 strpos() 函数上栽跟头喽(其实我现在还经常自作聪明,并且为其付出代价,然后继续自作聪明)。

OK,经过一番测试,感觉上是没什么大问题了,所以把代码交了出去。过一会,别人小小测试一下,发现类似百度和谷歌的搜索结果的网址无法验证通过,又是一波紧急研究。

结论是 filter_var() 会认定所有带有中文的网址不合法,虽然可以通过转义中文部分的方法强行通过验证,但我之前已经为了避免重复所以取消了所有 URL 的转义啊,这想想感觉会弄得好混乱。而且过会又发现,即使是在没有中文的情况下,它对锚点(就是网址中「#」后边的那一堆)链接的支持还有些迷之问题,所以果断抛弃之。

Happy End

好,重头戏来了。

无奈,只好继续去搜索解法,偶然间看到了一个网址,进去之后感觉进入了天堂一样,相见恨晚,里边简直有我现在所急需的一切。

如何正确的使用正则表达式验证网址链接

这个页面的作者搜集了很多不同的正则表达式,并用它们对一系列网址进行测试,有的网址需要验证通过,也有恰恰相反的。

最终,只有一位叫 Diego Perini 的选手通过了比赛,完美的达到了所有要求,他的正则表达式足足有 502 个字符,打眼一瞧非常的吓人,反正我是完全看不懂……

为了方便使用,我把它的正则表达式封装成了一个 PHP 函数,想要使用的话,直接调用就行了:

/**
 * 检测网址是否合法
 *
 * @link https://www.bgbk.org/regex-url/ 
 * @link https://gist.github.com/dperini/729294
 *
 * @param string $url 需要检测的网址。
 * @return bool 是否为一个合法的网址。
 */
function is_url( $url ) {
	if ( !trim( $url ) )
		return false;

	if ( strlen( $url ) < 10 )
		return false;

	$pattern = '_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iuS';

	$result = preg_match( $pattern, $url );
	$result = (bool) $result;

	return $result;
}

就像这样:

if ( is_url( $url ) )
	echo '可以,这很网址。'; // 验证成功
else
	echo '这是什么鬼?'; // 验证失败

虽然我对正则表达式理解不多,但它看长度就显然是巨耗费性能的,所以我先对网址进行了一些简单的预判断,希望可以减少服务器的压力。

另外,如果你想了解它的原理,或者想在 JavaScript 中使用这个正则表达式的话,可以参考作者在 GitHub 上的一个页面,里边有原版的 JavaScript 版本,并且附有大量注释,方便你学习和使用!

除此之外,你可以点击下载一份所有测试的 URLs 列表,可以亲自尝试各种正则表达式,或者验证你的方法,如果找到了更好的办法,一定要在评论中分享给大家哦!

附赠资料

最后附赠一个也是偶然发现的页面,里边用非常清晰的方式详细的介绍了网址的构成规则,感兴趣的同学可以点击此处去参观参观。

如何正确的使用正则表达式验证网址链接

在 javaScript 中,用 window.location. 加上演示模型里提到的属性名,就可以获取到当前页面网址的那一部分啦,比如 window.location.protocol,一般就会返回「http:」或「https:」。

当然,上边的网址只是展示了网址基本的构成元素,如果你想了解的更多,比如网址每个部分不能出现的字符啊、需要转义的文本啊,以及各种约定俗称的规则和注意事项,都可以在这里找到,非常完整哦!

评论

25条评论
  1. Gravatar 头像

    语音ic 回复

    看了半天还是不知道正则证明写?

  2. Gravatar 头像

    幻想影院 回复

    有勇气承担命运这才是英雄好汉。——黑塞

  3. Gravatar 头像

    亚奥地板 回复

    谢谢博主的分享,学到了,解决了我的很多疑惑。

  4. Gravatar 头像

    阳光电影 回复

    如果你想了解的更多,比如网址每个部分不能出现的字符啊

  5. Gravatar 头像

    夕凉 回复

    不错呢! 也正好需要.

  6. Gravatar 头像

    yy4410 回复

    我也表示真心看不懂!小编厉害

  7. Gravatar 头像

    五卡 回复

    厉害,一点也看不懂

发表评论

电子邮件地址不会被公开。 必填项已用*标注