正则训练营|1.正则学习整体脉络与历史小知识

2,136 阅读7分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

王志远,微医前端技术部

前言

​ 正则其实就是规则的设定,用于验证或者获取信息。匹配动作最粗暴的无疑是一一对应,a 对 a,b 对 b,这个规则就是完全一样才匹配,但这无疑太低效;而正则中的处理方式就是设定子规则,让某些符号不再代表本身,而是代表一些子规则,就像搭建大楼,我们希望找到自己想要的规则,就得用合适的最小砖块,再设定使用量,就搭建完成了。

​ 这个子规则,就是元字符,如我们常见的\d,代表单个 0-1 数字,\s就是换行指标等空白字符;需要注意的是,我们刚刚一直在强调单个,这其实也很好理解,对于文本而言,最小单元自然是单个字符,有了最小单元,我们再加上重复规模,就可以搭建我们自己的大楼了。

元字符

​ 那我们先来了解元字符,有四个维度,分别是【字符组】、【取反字符组】、【常用字符组】、【空白字符】,可以记忆为 【3 + 1】

量词

​ 理解了字符组,我们就要了解规模了,这在正则中有个术语 --- 量词还是【3+1】

量词匹配模式

​ 量词还涉及到模式问题,因为量词有范围,这就意味着可取多可取少,但计算机是不允许有歧义的,所以量词存在三种模式

正则匹配模式

​ 既然量词有模式,正则本身自然也有模式,针对【大小写、多行、点通配、备注】情况,存在【3+1】种模式

断言

​ 就像定位,不止需要本身的绝对信息,还需要看他的相对位置信息,这个信息在正则中叫断言,存在三种情况,【行首尾、单词边界和环视】,其中环视又存在前后是不是四种情况

编程

​ 到此我们才大致理解了元字符。

​ 就像编程语言,我们有了零碎的物料是不够的,还需要逻辑,在正则中存在分支语句|

​ 也存在提升优先级的分组,也即小括号,重点在两个:整体化和复用;

​ 使用也是两点:编号和命名;

  • 编号的定义很简单,就是数第几个左括号,编号就是几;使用在正则中是\编号,js 中使用是$编号
  • 命名的定义则是用(?<name>)的形式,其中 name 是变量名;正则中访问使用\k<name>,方法中访问使用$<name>

口诀彩蛋

3 + 1 元字符

3 + 1 常用元字符

3 + 1 量词

3 量词匹配模式

3 + 1 正则匹配模式

3-4 断言

2+2 编程

正则历史小知识

关于正则的历史

真正起源于神经网络

​ 20 世纪 40 年代,两位神经生理学家研究出了一种用数学方式描述神经网络的方法,并以此在 1956 年发表了一篇论文《正则表达式搜索算法》,主要描述了一种叫做**正则集合(Regular Sets)**的符合。

在计算机世界大放异彩

​ UNIX 之父在十年后的 1968 年,发表文章《正则表达式搜索算法》,并将正则移植到了大名鼎鼎的文本搜索工具grep中。

标准化历程

POSIX 流派

​ 1986 年出现的 POSIX 做出了很多规范,定义了 Unix 操作系统应当支持的功能,其中包含正则表达式的规范。

​ 这就意味着 Unix 派系系统上大部分工具的正则都遵循这个标准,称为POSIX流派。

PCRE 流派

​ 一年后,即 1987 年 12 月,Larry Wall 发布了Perl语言第一版,但其语言有着另一套正则标准,在细节上有很大差别,Perl语言走红所带来的的影响就是出现了另一个流派PCRE --- Perl 兼容正则表达式(Perl Compatible Regular Expressions)

接下来,我们分别了解下这两个流派的具体。

流派分析

POSIX 流派

​ 这个流派的主要适用方为 LINUX 派系,posix 流派自身存在两种标准:

  • BRE 标准(Basic Regular Expression 基本正则表达式)
  • ERE 标准(Extended Regular Expression 扩展正则表达式)

而 linux 发行版大多继承了 GNU 套件,GNU 在实现 POSIX 标准时,做了一定的扩展。那就存在了三方:BRE 标准、ERE 标准和 GNU 实现,这三者之间的关系如下图

此外,POSIX 流派还有自己独有的字符组

PCRE 流派

​ 目前大多数编程语言都是基于此标准,参考我们自己在语言中的使用即可。需要注意的是兼容问题,每个语言或工具都有这自己对标准的实现,分为直接兼容和间接兼容。

**直接兼容:**PCRE 流派中与 Perl 正则表达式直接兼容的语言或工具。比如 Perl、PHP preg、PCRE 库等,一般称之为 Perl 系。

**间接兼容:**比如 Java 系(包括 Java、Groovy、Scala 等)、Python 系(包括 Python2 和 Python3)、JavaScript 系(包括原生 JavaScript 和扩展库 XRegExp)、.Net 系(包括 C#、VB.Net 等)等。

特有的 POSIX 流派:LINUX

​ PCRE 流派我们很熟悉,因为每天用到的语言中正则就是遵循这个标准,但 linux 则不同,我们需要留意下差异,避免一脸懵。

linux 功能对应正则标准梳理

​ 在遵循 POSIX 规范的 UNIX/LINUX 系统上,按照BRE 标准实现的有 grep、sed 和 vi/vim 等,而按照 ERE 标准实现的有 egrep、awk 等等。

​ 在 linux 中也存在PCRE流派的使用,整理如下表

​ 其中有些工具实现了兼容标准,如grepsed,如果使用时加上-E,就是ERE标准;如果是-P就是PCRE标准。

# ERE 标准
grep -E '[[:digit:]]+' access.log
# PCRE 标准
grep -P '\d+' access.log
linux 正则标准查询

​ 在使用命令时,如何知道对应标准流派?使用man命令就可以进行查询啦

man [命令名]

返回结果对应如下

  • -G:BRE 标准
  • -E:ERE 标准
  • -P:PCRE 标准

实战加深思考

关于模式引出的一个问题

​ 如果你在 Linux 系统的一些命令行中使用正则,比如使用 grep 过滤内容的时候,你可能会发现结果非常诡异,就像下图这样,在 grep 命令中,使用正则\d+ 取不到数据,甚至在 egrep 中输出了英文字母 d 那一行。

​ 根据上文的学习,我们搞懂了各流派的差异,以及命令实现的是哪个正则标准。在 grep 中使用 \d+ 查找不到结果,是因为 grep 属于 BRE 流派,不支持 \d 来表示数字,加号也要转义才能表示量词的一到多次,所以无法找出数字那一行。如果你一定要用 BRE 流派,可以通过使用POSIX字符组和转义字符来实现。而 egrep 属于 ERE 流派,也不支持 \d,\d 相当于字母 d,所以找到了字母那一行。

​ 解决方案也很简单,在 grep 命令中,你可以指定参数 -P 来使用 PCRE 流派,这样就和我们之前学习到的是一致的了。知道了原因之后,你应该能写出相应的解决方法。下图是一些能工作的方法。

思考题 1

**题干:**使用下面的文本,在 Linux 中使用 grep 命令练习查找包含三个数字的行

123
1234
12345
123456
abcdef
\d
\d+
d+

可以使用如下命令

grep -P '\b\d{3}\b' [filename]

思考题 2

题干: 使用下面的文本,在 Linux 上使用 grep 命令,来查找含有 ftp、http 或 https 的行

https://time.geekbang.org
ftp://ftp.ncbi.nlm.nih.gov
www.baidu.com
www.ncbi.nlm.nih.gov

可以使用如下命令

 grep -P 'ftp|https?' [filename]