pascal-lc

须知少日拏云志,曾许人间第一流。

0%

从正则表达式匹配 IP 地址谈起

正则表达式 RegEx 是一种强大的字符串处理工具,在字符串处理中应用广泛。RegEx(Regular Expression)可以理解为匹配规则(Regular)的搜索模式。正则表达式匹配的不是字符串本身,而是一种字符串生成规则。凡是符合这一规则的字符串,都可以使用正则表达式检索获取。

介绍完正则表达式的概念,这里可以通过一些实践来体会正则表达式的强大与便捷。下面介绍如何使用正则表达式匹配 IP 地址,这里作为练习只处理 IPv4 地址。

待匹配字符串如下:

$ arp -a

接口: 192.168.216.1 --- 0x3
  Internet 地址         物理地址              类型
  192.168.216.255       ff-ff-ff-ff-ff-ff     静态
  224.0.0.22            01-00-5e-00-00-16     静态
  224.0.0.251           01-00-5e-00-00-fb     静态
  224.0.0.252           01-00-5e-00-00-fc     静态
  239.255.255.250       01-00-5e-7f-ff-fa     静态

接口: 169.254.86.111 --- 0xd
  Internet 地址         物理地址              类型
  169.254.255.255       ff-ff-ff-ff-ff-ff     静态
  224.0.0.22            01-00-5e-00-00-16     静态
  224.0.0.251           01-00-5e-00-00-fb     静态
  224.0.0.252           01-00-5e-00-00-fc     静态
  239.255.255.250       01-00-5e-7f-ff-fa     静态

接口: 192.168.1.2 --- 0xe
  Internet 地址         物理地址              类型
  192.168.1.1           18-13-2d-2f-fa-b4     动态
  192.168.1.255         ff-ff-ff-ff-ff-ff     静态
  224.0.0.22            01-00-5e-00-00-16     静态
  224.0.0.251           01-00-5e-00-00-fb     静态
  224.0.0.252           01-00-5e-00-00-fc     静态
  239.255.255.250       01-00-5e-7f-ff-fa     静态
  255.255.255.255       ff-ff-ff-ff-ff-ff     静态

接口: 169.254.252.27 --- 0x14
  Internet 地址         物理地址              类型
  169.254.255.255       ff-ff-ff-ff-ff-ff     静态
  224.0.0.22            01-00-5e-00-00-16     静态
  224.0.0.251           01-00-5e-00-00-fb     静态
  224.0.0.252           01-00-5e-00-00-fc     静态
  239.255.255.250       01-00-5e-7f-ff-fa     静态
  255.255.255.255       ff-ff-ff-ff-ff-ff     静态

初级

根据 IPv4 地址的格式,可以发现,IP 地址有 4 组数字组成,每组数字长度范围为从 1 位到 3 位,并且每组数字之间由 . 分割。那么根据这一规律,可以使用正则表达式写出匹配字符串:

((\d{1,3})\.){3}(\d{1,3})
(\d{1,3}\.){3}(\d{1,3})

IP 地址由 4 个字节组成,每个字节 8 位,所以 IPv4 地址的取值范围是 0 ~ 255,共 256 个。

这样的处理方法虽然简单,一目了然,但却需要面对一个大问题——正则表达式匹配出来的不是 IPv4 地址。例如:

999.888.777.666
......

这些非法 IPv4 地址字段也会在这一不严谨的正则表达式中匹配出来,这显然不是我们想要的。因此,我们需要对正则表达式进行改进,使其只匹配合法的 IPv4 地址。

高级

在构造一个正则表达式的时候,一定要考虑周全,避免出现匹配到不合法的字段,定义清楚的规则,确定可以匹配什么,以及不能匹配什么。

在这个 IPv4 地址匹配的过程中,我们需要确定一个合法有效的 IPv4 地址的格式,即:

  1. 4 组数字,每组数字范围为 1 ~ 255;
  2. 每组数字之间用 . 分割;
  3. 任意的 1 位或 2 位数字
  4. 任意的以 1 开头的 3 位数字;
  5. 任意的以 2 开头,第二位数字在 04 之间的 3 位数字;
  6. 任意的以 2 开头,第二位数字为 5,第三位数字在 05 之间的 3 位数字;

知晓上述规则后,依次罗列所有规则,准确的匹配模式自然可以构造如下:

(((\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]))

其中,按照 IPv4 地址规则,

  • (\d{1,2}) 匹配任意 1 位或 2 位数字;
  • (1\d{2}) 匹配任意以 1 开头的 3 位数字;
  • (2[0-4]\d) 匹配任意以 2 开头,第二位数字在 04 之间的 3 位数字;
  • (25[0-5]) 匹配任意以 25 开头的 3 位数字;

看起来不错,完全按照上述 IPv4 规则匹配,但只要实际匹配查找,会发现上述正则表达式,根本无法正常匹配 IPv4 地址,效果甚至不如第一个匹配模式。例如:

255.255.255.0
255.255.255.10
255.255.255.255

这两种 Ipv4 地址,上述正则表达式只能匹配到第一个和第二个,却无法匹配到第三个。但是,正则表达式规则是严格按照 IPv4 地址规则定义的,为什么会出现这种情况呢?

这就要引出正则表达式的贪心(greedy)特性了。也就是在正则表达式中,如果出现多个匹配模式,那么会优先匹配最长的匹配模式,而不是符合匹配规则的最短的,而且会尽量满足第一个匹配模式,具体到 IPv4 地址匹配,就是尽可能匹配最长的规则以及首位规则。

((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))

这是匹配一组 IPv4 地址字段的正则表达式匹配模式,其中 (\d{1,2}) 表示匹配任意 1 ~ 2 位数字,在 IPv4 前三组字段的匹配中,由于 . 的存在,根据正则表达式的贪心特性,会尽可能匹配最长的匹配模式,即 (\d{1,2}) 无法匹配的会自动切换下一子表达式,以求匹配的字符串模式更长。所以,上述正则表达式匹配前三组没有出现任何问题,但是在第四组字段的匹配过程中,由于 (\d{1,2}) 无法匹配 255,所以当 (\d{1,2}) 匹配 25 时,两个数字全部匹配到,匹配模式自动结束,如果第四个字段是 3 个数字,那么第三个数字将被忽略而无法匹配到。

那么,如何改进呢?

进阶

其实,根据以上分析,可以发现,无需较大修改,因为 IPv4 规则仍是不变的,如果将 IPv4 地址的匹配规则改为如下,就可以完美匹配了。

(((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))\.){3}(((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2})))

按照正则表达式的贪心特性以及首位子表达式优先匹配特性,可以在规则组合时,优先选择匹配位数较长的子表达式,当前子表达式不满足时,在依次切换匹配位数较短的子表达式。

写在最后

本来只是正则表达式匹配 IPv4 地址,一个很简单的小练习,没想到发现了一个正则表达式匹配 IPv4 地址的坑,涉及到正则表达式的贪心特性以及首位子表达式优先。世事洞明皆学问,此言不虚,所以,还是记录一下吧。

1. 如何用 C 语言画这个图

1.1. 原问题

求大神这个图案怎么用 C 语言编写?


                      *                       
                    * * *                     
                  * * * * *                   
                *           *                 
              * * *       * * *               
            * * * * *   * * * * *             
          *           *           *           
        * * *       * * *       * * *         
      * * * * *   * * * * *   * * * * *       
    *           *           *           *     
  * * *       * * *       * * *       * * *   
* * * * *   * * * * *   * * * * *   * * * * *
阅读全文 »

Markdown 基本要素

这篇文件意在简要介绍 GitHub Flavored Markdown 写作。

什么是 Markdown?

Markdown 是一种文本格式。你可以用它来控制文档的显示。使用 markdown,你可以创建粗体的文字,斜体的文字,添加图片,并且创建列表 等等。基本上来讲,Markdown 就是普通的文字加上 # 或者 * 等符号。

阅读全文 »

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

$ hexo new "My New Post"

More info: Writing

Run server

$ hexo server

More info: Server

Generate static files

$ hexo generate

More info: Generating

Deploy to remote sites

$ hexo deploy

More info: Deployment