福建厦门赵 發表於 2022-12-2 10:56:27

正则表达式匹配ip地址超详细讲解

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、正则匹配基本知识及概念</a></li><li><a href="#_label1">二、ip地址匹配</a></li><li><a href="#_label2">附正则表达式匹配IP地址小结</a></li><li><a href="#_label3">总结</a></li></ul></div><p class="maodian"><a name="_label0"></a></p><h2>一、正则匹配基本知识及概念</h2>
<p>在练习之前,需要大家知道一些基本知识,如果有一定基础的可以跳过该步骤,直接往下看。</p>
<p>正则表达式-字符类</p>
<blockquote><p>:代表a或者b,或者c字符中的一个。<br />[^abc]:代表除a,b,c以外的任何字符。<br />:代表a-z的所有小写字符中的一个。<br />:代表A-Z的所有大写字符中的一个。<br />:代表0-9之间的某一个数字字符。<br />:代表a-z或者A-Z或者0-9之间的任意一个字符。<br />:a 到 d 或 m 到 p之间的任意一个字符。</p></blockquote>
<p>正则表达式-逻辑运算符</p>
<blockquote><p>&amp;&amp;:并且<br />| :或者(可以省略)</p></blockquote>
<p>正则表达式-预定义字符</p>
<blockquote><p>&ldquo;.&rdquo; : 匹配任何字符。<br />&ldquo;\d&rdquo;:任何数字的简写;<br />&ldquo;\D&rdquo;:任何非数字[^0-9]的简写;<br />&ldquo;\s&rdquo;: 空白字符:[ \t\n\x0B\f\r] 的简写<br />&ldquo;\S&rdquo;: 非空白字符:[^\s] 的简写<br />&ldquo;\w&rdquo;:单词字符:的简写<br />&ldquo;\W&rdquo;:非单词字符:[^\w]</p></blockquote>
<p>正则表达式-数量词</p>
<blockquote><p>x? : 0次或1次<br />x* : 0次到多次<br />x+ : 1次或多次<br />X{n} : 恰好n次<br />X{n,} : 至少n次<br />X{n,m}: n到m次(n和m都是包含的,最少n次,最多m次。</p></blockquote>
<p class="maodian"><a name="_label1"></a></p><h2>二、ip地址匹配</h2>
<p><strong>题目要求:使用正则表达式匹配192.11.23.69</strong></p>
<p>须知:ip地址的范围为0.0.0.0-255.255.255.255</p>
<p>接下来我们直接进入正题吧!在我们看到题目的第一眼,大家可能觉得很简单,这不就用<strong>\d{1,3}\.\d{1,3}\.\d{1,3}\.</strong>\d{1,3}</p>
<p style="text-align:center"><img alt="zh" src="https://img.jbzj.com/file_images/article/202212/2022120210464361.png" /></p>
<p>这里我们可以看到使用上述表达式的确匹配成功。</p>
<p>虽然完成了我们的题目要求,有些细心的伙伴会发现,用红色标注出来的部分,重复了三遍,是不是我们可以用<strong>分组</strong>的方式来简化表达式并完成题目要求。我们把上面的匹配表达式换成分组之后的样子,即<strong>(\d{1,3}\.){3}\d{1,3}</strong></p>
<p>表达式讲解:(\d{1,3}\.) 匹配我们前三段的ip地址和&rsquo;.&lsquo;,因为第四段ip地址最后面不能有&rsquo;.&#39;,如果我们使用在同一个分组去匹配就会出现错误情况,而\d{1,3}刚好匹配我们最后一段ip地址</p>
<p>匹配效果如下:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464362.png" /></p>
<p>&nbsp;匹配同样成功,该模式有什么不对的地方吗?从语法上看,完全正确,也达到了我们的题目要求,可是我们有没有注意到,我们日常生活中所用的ip地址每一位都是在0-255之间,但是我们上面写的表达式会匹配0-999之间的所有ip地址,如果在未来的工作中,有这样的需求,要匹配所有合法的ip地址,那么我们上面所写的正则表达式是不是就不符合要求了,这里就需要我们使用子表达式的嵌套(注意:有一点很重要。通过上面的例子,我们发现,写一个能够匹配预期内容的正则表达式其实并不难,但是写一个能够考虑到所有可能场景,确保将不需要匹配的内容排除在外的正则表达式就困难多了)</p>
<p>&nbsp;所以我将上面的表达式又改进了一下(这里用到了子表达式的嵌套,如果不懂的小伙伴可以先看一下基本概念再来看接下来的内容)。我们发现当ip地址仅有一位或者两位的时候(即1.1.1.1 or 11.11.11.11),用(\d{1,2}\.)就可以完成匹配,当ip地址为三位的时候,会有这么两种情况(这里留下个疑问,考虑到初学者可能犯错的情况)?</p>
<p>1:ip地址开头为1的时候,我们后面的两位每一位的范围都在0-9之间,而\d这个元字符刚好满足了我们的要求,所以使用(1\d{2}.)就满足了我们在100-199ip地址的匹配,这个其实还相对简单,接下来就是200-255之间ip地址的匹配了,有些同学可能会想,我们可以使用匹配100-199的表达式来实现对200-255ip地址的匹配,即(2\d{2}\.)这样的表达式来实现,可是这样会把256-299之间的ip地址匹配到,违反了我们的意愿,所以我改良了一下表达式(2{2}\.]),这样就只会匹配到200-255之间的ip地址,既然子表达式都写好了,就让我们来实践看看效果吧!</p>
<p>表达式为:((1\d{2}.)|(2{2}.)|(\d{1,2}.)){3}((\d{1,2})|(1\d{2})|(2{2}))),伙伴们可以一起实践一下!</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464363.png" /></p>
<p>匹配成功,可是再写出这个表达式,我们要测试某些特殊情况是否不在我们的匹配范围内,于是我发现了以下两个比较重要的问题。</p>
<p>第一种:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464364.png" /></p>
<p>合法ip地址匹配错误</p>
<p>第二种:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464365.png" /></p>
<p>非法ip地址匹配成功</p>
<p>为什么会出现上面的情况?</p>
<p>当我们觉得表达式很完美的时候,虽然256超出匹配范围,但是因为(\d{1,2}\.)的影响,使得我们的非法表达式也匹配成功,本来192.11.23.200为合法表达式,却只匹配到了98.11.23.200,请大家看我用红色箭头标注的地方,会不会跟我们子表达式的顺序有关呢?那好,我来改变一下顺序,我们将(\d{1,2}\.)放到了最后,防止出现错误匹配</p>
<p>改变匹配顺序后的表达式为:<br />( ( (2{2}.)|(1\d{2}.)|(\d{1,2}.)){3}((1\d{2})|(2{2})|(\d{1,2}))<strong>)</strong></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464466.png" /></p>
<p>果然,第一种情况我们是因为子表达式顺序的原因,导致匹配错误。</p>
<p>可是第二种情况使用改变顺序的表达式,依然会出现以上问题(这里也就印证了上文提到的那些话,要想写出一个符合规范的表达式就很困难)。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464467.png" /></p>
<p>于是这里的^就起到了作用,我们可以把第一个子表达式单另拉出来加上^表示第一段ip地址开头为(\d{1,2}),这样刚好避免了我们如上的情况,效果如下:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202212/2022120210464468.png" /></p>
<p>可以看到非法ip不在我们的匹配当中。</p>
<p>其实写到这里大家是不是感觉已经大功告成了?回答:No</p>
<p>(2{2})这个表达式能否匹配239这个ip呢?不知道有没有细心的小伙伴发现</p>
<p>所以最终正确的表达式应该为:<br /><strong>(^((2\d.)|(25.)|(1\d{2}.)|(\d{1,2}.))((2{2}.)|(1\d{2}.)|(\d{1,2}.){2})((1\d{2})|(2{2})|(\d{1,2})))</strong></p>
<p>在这里可以跟大家说一下,是否可以使用$来避免我们上述第一种问题,有兴趣的可以尝试一下</p>
<p class="maodian"><a name="_label2"></a></p><h2>附正则表达式匹配IP地址小结</h2>
<p><strong>1. IP段都表示一个字节,即只能在 0~255之间。</strong></p>
<p>所以一个正确的IP应该是:(0~255) .(0~255) .(0~255) .(0~255)</p>
<p>通过观察可以发现可以将整个IP分为两部分匹配,即:(0~255) 和 .(0~255) 3次</p>
<p><strong>2. 0~255可以分两部分匹配 0~199 和 200~255</strong></p>
<p>a) 0~199 正则表达式为 ?\d{1,2}</p>
<p>? 表示匹配 0或1一次或零次</p>
<p>\d 表示匹配任意一个十进制数字,即 0~9</p>
<p>{1,2} 表示匹配上一个元素至少一次,最多两次,这里就是 \d一次或两次</p>
<p>b) 200~255 正则表达式为 2((5)|(\d)),又可以分为两部分 200~249 和 250~255</p>
<p>2 表示必须以2开头</p>
<p>5 表示匹配 50~55 之间的数</p>
<p>\d 表示 00~49 之间的数</p>
<p><strong>3.三个部分的正则匹配</strong></p>
<p>a) (0~255) 的正则表达式可以写为 (2((5)|(\d)))|(?\d{1,2})</p>
<p>b) .(0~255) 的正则表达式可以写为 .((2((5)|(\d)))|(?\d{1,2}))</p>
<p>c) .(0~255) 匹配3次的正则表达式可以写为 (.((2((5)|(\d)))|(?\d{1,2}))){3}</p>
<p class="maodian"><a name="_label3"></a></p><h2>总结</h2>
頁: [1]
查看完整版本: 正则表达式匹配ip地址超详细讲解