29. 正则表达式之ip地址匹配

白天和小伙伴聊起明后天就要去瓜子和蚂蜂窝面试,他说真棒,瓜子我朋友刚去过,蚂蜂窝去年我去过,没成,蚂蜂窝可是被称为望京的小阿里巴巴呢。当时问了很多Web端的东西,这方面不怎么懂。说让我写个正则,匹配邮箱和ip地址,不会写。想起前几次面试,也是同样的问题,就让写个匹配邮箱的正则,我都是空着。还问我,你不会写正则吗?恩,很少写了。像这样的正则其实是应该背下来的,虽然用的不多,但是毕竟问过好多次了。刚好想起前年读过的一本小书《正则表达式必知必会》,其中就有这篇关于ip地址匹配,当时觉得挺有趣,还抄写下来,终于从尘封的电脑文档中找出来了。

有这样一段文本:

Pinging hog.forta.com [12.159.46.200] with 32 bytes of data.

匹配出其中的ip地址,简单粗暴的处理方式是:

$pattern = '/(\d{1,3}\.){3}\d{1,3}/';

但是注意,不可以使用下面的方式:

$pattern = '/(\d{1,3}\.){4}/';

因为至少要保证ip地址的格式是以英文句号分隔的四组数字,每组数字由1个,2个或者3个数字字符构成。

从语法上讲,这个匹配ip地址的正则表达式是没有问题的,它能匹配到所有合法的ip地址。但是深入来说,这个正则表达式也能匹配到非法的ip地址。ip地址中每组数字的取值范围是有限制的,即0-255之间。

把必须匹配的情况考虑周全并写出一个匹配结果符合预期的正则表达式很容易,但把不需要匹配的情况也考虑周全并确保它们都将被排除在匹配结果以外往往要困难得多。

如果有办法设定各种取值范围的话,事情会简单得多,但可惜的是正则表达式只是一种工具,而且还是一种不懂数学运算的工具,它们在匹配字符的时候并不真正关心哪些字符到底是什么以及有什么含义,你的数学能力再好在这里也帮不上忙。

真的没有解决这个问题的办法吗?未必,只要你们能够充分发挥你们的逻辑思维能力,就能解决与正则表达式有关的任何难题。这里的基本思路是:在构造一个正则表达式的时候,一定要把你想匹配什么和你不想匹配什么详尽地定义清楚。下面是一个合法的ip地址里的各组数字必须且只能符合的规则:

  1. 任何一个1位或2位的数字
  2. 任何一个以1开头的3位数字
  3. 任何一个以2开头,第2位数字在0-4之间的3位数字
  4. 任何一个以25开头,第3位数字在0-5之间的3位数字

构造出来的正则表达式就是:

$pattern = '/(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))/';

我在网上看过一些自称是常用正则表示式整理的文章,很多书写都有问题,包括手机号,邮箱,是否是合法的请求路径,都存在各种各样的问题,虽然都能使用,但并不总是正确的。

摘自《正则表达式必知必会》 (美)福达(forta,B.)著,杨涛等译 –2版 北京:人民邮电出版社。