You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 07 | 正则有哪些常见的流派及其特性?
你好,我是涂伟忠。今天我来给你讲讲正则常见的流派及其特性。
你可能要问了,讲正则流派有啥用呢?不如多来点实战啊。其实,我们去了解正则的演变过程是很有必要的。因为你一旦了解了正则的演变过程之后,就能够更加正确地去使用正则,尤其是在 Linux系统中。
那我们就先来看一个有关Linux系统的例子你先来感受一下。
如果你在 Linux 系统的一些命令行中使用正则,比如使用 grep 过滤内容的时候你可能会发现结果非常诡异就像下图这样在grep命令中使用正则\\d+取不到数据,甚至在 egrep 中输出了英文字母d那一行。
![](https://static001.geekbang.org/resource/image/f1/09/f183b6fb3fba964ab9a9c3f8aa159b09.png)
这个执行结果的原因就和正则的演变有着密不可分的关系。那到底有什么样的关系呢?我们接着往下看,我从正则的发展历史给你讲起。
## 正则表达式简史
正则表达式的起源可以追溯到早期神经系统如何工作的研究。在20世纪40年代有两位神经生理学家Warren McCulloch和Walter Pitts研究出了一种用数学方式来描述神经网络的方法。
1956年一位数学家Stephen Kleene发表了一篇标题为《[神经网络事件表示法和有穷自动机](https://www.doc88.com/p-9763182364861.html)》的论文。这篇论文描述了一种叫做“正则集合Regular Sets”的符号。
随后大名鼎鼎的Unix之父Ken Thompson于1968年发表了文章《[正则表达式搜索算法](https://www.fing.edu.uy/inco/cursos/intropln/material/p419-thompson.pdf)》并且将正则引入了自己开发的编辑器qed以及之后的编辑器ed中然后又移植到了大名鼎鼎的文本搜索工具grep中。自此正则表达式被广泛应用到Unix系统或类Unix系统(如macOS、Linux)的各种工具中。
随后,由于正则功能强大,非常实用,越来越多的语言和工具都开始支持正则。不过遗憾的是,由于没有尽早确立标准,导致各种语言和工具中的正则虽然功能大致类似,但仍然有不少细微差别。
于是诞生于1986年的POSIX开始进行标准化的尝试。[POSIX](https://standards.ieee.org/develop/wg/POSIX.html)作为一系列规范定义了Unix操作系统应当支持的功能其中也包括正则表达式的规范。因此Unix系统或类Unix系统上的大部分工具如grep、sed、awk等均遵循该标准。我们把这些遵循POSIX正则表达式规范的正则表达式称为**POSIX流派**的正则表达式。
在1987年12月Larry Wall发布了Perl语言第一版因其功能强大一票走红所引入的正则表达式功能大放异彩。之后Perl语言中的正则表达式不断改进影响越来越大。于是在此基础上1997年又诞生了[PCRE](http://www.pcre.org/)——**Perl兼容正则表达式**Perl Compatible Regular Expressions
PCRE是一个兼容Perl语言正则表达式的解析引擎是由Philip Hazel开发的为很多现代语言和工具所普遍使用。除了Unix上的工具遵循POSIX标准PCRE现已成为其他大部分语言和工具隐然遵循的标准。
之后,正则表达式在各种计算机语言或各种应用领域得到了更为广泛的应用和发展。**POSIX流派** 与 **PCRE流派** 是目前正则表达式流派中的两大最主要的流派。
## 正则表达式流派
就像前面说的一样目前正则表达式主要有两大流派FlavorPOSIX流派与PCRE流派。下面我们分别来看看。
### 1\. POSIX流派
这里我们先简要介绍一下POSIX流派。POSIX 规范定义了正则表达式的两种标准:
* **BRE标准**Basic Regular Expression 基本正则表达式);
* **ERE标准**Extended Regular Expression 扩展正则表达式)。
接下来,我们一起来看一下这两种标准的异同点。
#### BRE标准 和 ERE标准
早期BRE与ERE标准的区别主要在于BRE标准不支持量词问号和加号也不支持多选分支结构管道符。BRE标准在使用花括号圆括号时要转义才能表示特殊含义。BRE标准用起来这么不爽于是有了 ERE标准在使用花括号圆括号时不需要转义了还支持了问号、加号 和 多选分支。
我们现在使用的Linux发行版大多都集成了GNU套件。GNU在实现POSIX标准时做了一定的扩展主要有以下三点扩展。
1. GNU BRE支持了 +、?,但转义了才表示特殊含义,即需要用`\+、\?`表示。
2. GNU BRE支持管道符多选分支结构同样需要转义即用 `\|`表示。
3. GNU ERE也支持使用反引用和BRE一样使用 \\1、\\2…\\9 表示。
BRE标准和ERE标准的详细区别我给了你一个参考图你可以看一下浅黄色背景是BRE和ERE不同的地方三处天蓝色字体是GNU扩展。
![](https://static001.geekbang.org/resource/image/53/6f/53fe0982c70fe89dff733345a6816e6f.png)
总之GNU BRE 和 GNU ERE 它们的功能特性并没有太大区别,区别是在于部分语法层面上,主要是一些字符要不要转义。
#### POSIX字符组
POSIX流派还有一个特殊的地方就是有自己的字符组叫POSIX字符组。这个类似于我们之前学习的 \\d 表示数字,\\s表示空白符等POSIX中也定义了一系列的字符组。具体的清单和解释如下所示
![](https://static001.geekbang.org/resource/image/c3/ya/c32024952cb6af3f78d9c08d9b5b3yya.png)
### 2\. PCRE流派
除了POSIX标准外还有一个Perl分支也就是我们现在熟知的PCRE。随着Perl语言的发展Perl语言中的正则表达式功能越来越强悍为了把Perl语言中正则的功能移植到其他语言中PCRE就诞生了。
目前大部分常用编程语言都是源于PCRE标准这个流派显著特征是有\\d、\\w、\\s这类字符组简记方式。
不过虽然PCRE流派是从Perl语言中衍生出来的但与Perl语言中的正则表达式在语法上还是有一些细微差异比如PHP的preg正则表达式(Perl Regular Expression)与Perl正则表达式的差异可看[这里](http://php.net/manual/zh/reference.pcre.pattern.differences.php)。
考虑到目前绝大部分常用编程语言所采用的正则引擎基本都属于PCRE流派的现实情况我们的课程也是主要讲解PCRE流派。前面对于正则表达式语法元素的解释都是以PCRE流派为准。
#### PCRE流派的兼容问题
虽然PCRE流派是与Perl正则表达式相兼容的流派但这种兼容在各种语言和工具中还存在程度上的差别这包括了直接兼容与间接兼容两种情况。
而且即便是直接兼容也并非完全兼容还是存在部分不兼容的情况。原因也很简单Perl语言中的正则表达式在不断改进和升级之中其他语言和工具不可能完全做到实时跟进与更新。
* **直接兼容**PCRE流派中与Perl正则表达式直接兼容的语言或工具。比如Perl、PHP preg、PCRE库等一般称之为Perl系。
* **间接兼容**比如Java系包括Java、Groovy、Scala等、Python系包括Python2和Python3、JavaScript系包括原生JavaScript和扩展库XRegExp、.Net系包括C#、VB.Net等等。
## 在Linux中使用正则
在遵循POSIX规范的UNIX/LINUX系统上按照 **BRE标准** 实现的有 grep、sed 和 vi/vim 等,而按照 **ERE标准** 实现的有 egrep、awk 等。
在UNIX/LINUX系统里PCRE流派与POSIX流派的对比我为你整理了一个表你可以看一下。
![](https://static001.geekbang.org/resource/image/eb/85/ebfd65253886552f034c50da3674ce85.png)
刚刚我们提到了工具对应的实现标准,其实有一些工具实现同时兼容多种正则标准,比如前面我们讲到的 grep 和 sed。如果在使用时加上-E选项就是使用ERE标准如果加上-P选项就是使用PCRE标准。
```
使用 ERE 标准
grep -E '[[:digit:]]+' access.log
使用 PCRE 标准
grep -P '\d+' access.log
```
在使用具体命令时如何知道属于哪个流派呢你不用担心太多了记不住。在Linux系统中有个 man 命令可以帮助我们。比如,我在 macOS 上执行 man grep ,可以看到选项 -G 是指定使用 BRE标准默认-E是ERE标准-P是PCRE标准。所以在使用具体工具时你通过这个方法查一下命令的说明就好了。
![](https://static001.geekbang.org/resource/image/1d/bc/1d43a1287e7881b87428ede0f85b63bc.png)
我们再看开篇提出的问题。
通过今天的学习,我们搞懂了各流派的差异,以及命令实现的是哪个正则标准。在 grep 中使用 \\d+ 查找不到结果,是因为 grep 属于 BRE 流派,不支持 \\d 来表示数字加号也要转义才能表示量词的一到多次所以无法找出数字那一行。如果你一定要用BRE流派可以通过**使****用****POSIX字符组** 和 **转义加号** 来实现。而egrep属于ERE流派也不支持 \\d\\d 相当于字母 d所以找到了字母那一行。
![](https://static001.geekbang.org/resource/image/f1/09/f183b6fb3fba964ab9a9c3f8aa159b09.png)
在grep命令中你可以指定参数-P来使用PCRE流派这样就和我们之前学习到的是一致的了。知道了原因之后你应该能写出相应的解决方法。下图是一些能工作的方法。
![](https://static001.geekbang.org/resource/image/a3/b2/a3bbeb6aa533cd06ea5d8f3b9e0b96b2.png)
为了方便加深你的理解我给你提供了一个例子来帮你巩固。你可以使用下面的文本在Linux中使用grep命令练习查找包含一到多个数字的行。
```
123456
abcdef
\d
\d+
d+
```
## 总结
好了,今天的内容讲完了,我来带你总结回顾一下。
今天我带你简单回顾了下正则表达式的历史。正则主要有两大流派分别是POSIX流派和PCRE流派。其中POSIX流派有两个标准分别是BRE标准和ERE标准**一般情况下我们面对的都是GNU BRE和GNU ERE。它们的主要区别在于前者要转义。**另外, POSIX流派一个特点就是有自己的字符组POSIX 字符组,这不同于常见的 \\d 等字符组。
PCRE流派是如今大多数编程语言实现的流派最大的特点就是支持\\d\\s\\w等我们前面讲的内容也是基于这个流派进行的。
如果你需要在类Unix平台命令等上使用正则使用前需要搞清楚工具属于哪个标准比如grep、sed、vi/vim 等属于BRE标准egrep、awk 属于ERE标准。而sed -P、grep -P等属于PCRE流派。这些也不需要死记硬背使用时用man命令看一下就好了。
我在这里给你放了一张今天所讲内容的总结脑图,你可以看一下。另外我还给你提供了一个记忆小窍门,你可以着重记忆一下这句话:**GNU ERE名称中有两个E不需要再转义。而GNU BRE 只有一个E使用时“花圆问管加”时都要转义**。
![](https://static001.geekbang.org/resource/image/23/2f/239d9c1cc82d840b7b27492c7b4c222f.png)
此外我还给了你一个Linux/Unix工具与正则表达式的POSIX规范余晟的[参考链接](https://www.infoq.cn/article/2011/07/regular-expressions-6-POSIX/),你可以看一下。
## 课后思考
最后我们来做一个小练习吧。在Linux上使用grep命令分别实现使用不同的标准即 BRE、ERE、PCRE ),来查找含有 ftp、http 或 https 的行。你可以动手体验一下不同标准的区别。
```
https://time.geekbang.org
ftp://ftp.ncbi.nlm.nih.gov
www.baidu.com
www.ncbi.nlm.nih.gov
```
好,今天的课程就结束了,希望可以帮助到你,也希望你在下方的留言区和我参与讨论,并把文章分享给你的朋友或者同事,一起交流一下。