首页 » 网站推广 » php正则道理技巧_浅谈正则表达式从事理到实战

php正则道理技巧_浅谈正则表达式从事理到实战

访客 2024-11-21 0

扫一扫用手机浏览

文章目录 [+]

字符

描述

php正则道理技巧_浅谈正则表达式从事理到实战

举例

php正则道理技巧_浅谈正则表达式从事理到实战
(图片来自网络侵删)

锚点

^

匹配输入字符串开始的位置。
如果设置了多行匹配属性,^还会与\n 或 \r之后的位置匹配

^download能匹配"download_finish",但不能匹配"finish_download" 或 pownload_finish

$

匹配输入字符串结束的位置。
如果设置了多行匹配属性,$还会与\n 或 \r之前的位置匹配

download$能匹配"finish_download",但不能匹配"download_finish"

\A

在多行模式下表示全体文本的起始位置,JavaScipt不支持

\Z

在多行模式下表示全体文本的结束位置,JavaScipt不支持

量词

匹配前面的子表达式零次或多次(≥0)

zo能匹配"z"和"zoo"

+

匹配前面的子表达式一次或多次(≥1)

zo+能匹配"zo"和"zoo",但不能匹配"z"

?

匹配前面的子表达式零次或一次(0|1)

do(es)?能匹配"do"或"does"中的"do"

{n}

n是一个非负整数,匹配确定的n次。

o{2}不能匹配"Bob"中的'o',但能匹配"food"中的两个'o'

{n,}

n是一个非负整数,至少匹配n次。
{0,}等价于,{1,}等价于+

o{2,}不能匹配"Bob"中的'o',但能匹配"foooood"中的所有'o'

{n,m}

n,m均是非负整数,且n≤m,最少匹配n次且最多匹配m次。
{0,1}等价于?

o{1,3}匹配"foooood"的结果是:ooo,oo

?

紧跟上述任何量词后面时,匹配模式变为非贪婪。
默认是贪婪匹配 o{1,3}?匹配"foooood"的结果是:o,o,o,o,o

范围

x|y

匹配x或y

m|food能匹配m或food,(m|f)ood能匹配mood或food

[xyz]

字符凑集。
匹配所包含的任意一个字符

[abn]可以匹配"plain"中的a,n

[^xyz]

负值字符凑集。
匹配未包含的任意字符

[^abn]匹配"plain"的结果是p,l,i

[a-z]

字符范围。
匹配指定例模内的任意字符

[a-m]可以匹配a到m范围内的任意字符,匹配"plain"的结果是l,a,i

[^a-z]

负值字符范围。
匹配任何不在指定例模内的任意字符

[^a-m]可以匹配不在a到m范围内的任意字符,匹配"plain"的结果是p,n

简写字符

.

匹配除“\n”和"\r"之外的任何单个字符。
要匹配包括“\n”和"\r"在内的任何字符,请利用像“[\s\S]”的模式。

\d

匹配一个数字字符。
等价于 [0-9]

\D

匹配一个非数字字符。
等价于 [^0-9]

\s

匹配任何空缺字符,包括空格、制表符、换页符等等。
等价于 [ \f\n\r\t\v]

\S

匹配任何非空缺字符。
等价于 [^ \f\n\r\t\v]

\w

匹配包括下划线的任何单词字符。
等价于[A-Za-z0-9_]

\W

匹配任何非单词字符。
等价于 '[^A-Za-z0-9_]'

\B

匹配非单词边界

'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'

\b

匹配一个单词边界,也便是指单词和空格间的位置

'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'

其他字符

\

转义字符

, +, ?, \, (, )……

(pattern)

捕获型括号,匹配pattern,匹配pattern并捕获结果,自动获取组号

顺序为按照左括号从左向右的的顺序

(?:pattern)

非捕获型括号,匹配pattern,但不捕获匹配结果。

industr(?:y|ies)和industr(y|ies)表达的意思同等的,差异在于括号内的内容是否会被捕获。
不才文中会有详细先容。

值得把稳的是,正则表达式拥有诸多流派和标准,各大标准中的元字符虽大同小异,但也有细微差别,须要仔细甄别后利用。

量词与贪婪匹配

从上文可知,正则表达式的量词统共有六种,在这 6 种元字符中,我们可以用 {m,n} 来表示 、+、?这 3 种元字符:

元字符

同义表示方法

示例

{0,}

ab 可以匹配 a 或 abbb

+

{1,}

ab+ 可以匹配 ab 或 abbb,但不能匹配 a

?

{0,1}

(+86-)?\d{11} 可以匹配 +86-13800138000 或 13800138000

但在实践中,还有一些细微差别,如下例子:

对付正则表达式"a+",用它来匹配"aaabb",得到的匹配结果是"aaa"。

而对付正则表达式"a",用它来匹配"aaabb",得到的匹配结果除了"aaa",还有三个空字符串,这是由于是匹配0次到多次到,0次便是空字符串。
但是,为什么在"aaa"部分没有匹配空字符串呢,这里就引出了贪婪匹配和非贪婪匹配到观点。
贪婪匹配和非贪婪匹配的简要观点如下:

贪婪模式,尽可能进行最长匹配。
非贪婪模式,尽可能进行最短匹配。
贪婪匹配

在正则中,表示次数的量词 默认是贪婪的,在贪婪模式下,会考试测验尽可能最大长度去匹配。

首先,我们来看一下在字符串 aaabb 中利用正则"a"的匹配过程。

匹配

开始

结束

解释

匹配内容

第 1 次

0

3

到第一个字母 b 创造不知足,输出 aaa

aaa

第 2 次

3

3

匹配剩下的 bb,创造匹配不上,输出空字符串

空字符串

第 3 次

4

4

匹配剩下的 b,创造匹配不上,输出空字符串

空字符串

第 4 次

5

5

匹配剩下的空字符串,输出空字符串

空字符串

贪婪模式的特点便是尽可能得进行最大长度匹配。

非贪婪匹配

与贪婪模式相反,非贪婪模式尽可能进行最短长度的匹配。
在元字符后面加上?,就开启了非贪婪模式,我们还是用之前的例子:

可以看到,尽可能匹配了最小长度,每次都匹配了空字符串,终极的结果如上所示。

独占模式

贪婪模式和非贪婪模式都须要回溯才能完成相应的功能,下面用一个例子来阐明什么是回溯。

对付目标字符串"xxxyyy",正则表达式为"x{1,3}xy",分别来看它的匹配过程:

匹配模式

正则表达式

匹配过程

能否匹配

贪婪模式

x{1,3}xy

regex101.com/r/oGWple/1

非贪婪模式

x{1,3}?xy

regex101.com/r/6LYQz3/1

贪婪模式和非贪婪模式虽然回溯的办法不同,但都会进行回溯。

独占模式和贪婪模式很像,独占模式会尽可能多地去匹配,如果匹配失落败就结束,不会进行回溯,这样的话就比较节省韶光。
详细的方法便是在量词后面加上+。

用独占模式来匹配上面的例子,结果如下:

匹配模式

正则表达式

匹配过程

能否匹配

独占模式

x{1,3}+xy

regex101.com/r/BH3rSo/1

不能

可以看到,独占模式尽可能匹配了最大长度的x,当之后的xy无法被匹配时,本次匹配被放弃,终极导致无法匹配。

独占模式不会进行回溯,因此会有较好的性能,但也会导致一些情形不能被匹配,以及,不是所有措辞、所有标准都支持独占模式,须要剖析详细情形再进行选择。

回溯引发的性能问题

上文提到了贪婪匹配须要回溯才能完成相应匹配,而量词默认是贪婪匹配的。
我们设想这样一个场景:用".ab"来匹配"1abxxx","."的贪婪匹配会前辈行最长匹配,覆盖全体字符串,再一个个“吐”出字符,来匹配之后的"ab",若后面的xxx足够长,肯定会导致严重的性能问题,下面举两个例子来解释这一点:

很长的xxx:regex101.com/r/OU4IYf/1

可以看到,只管开头的"1ab"就已经可以进行匹配,但正则表达式还是用了200余次的回溯才完成匹配,那么,如果后面的xxx更长一些,会怎么样呢?

非常长的xxx:regex101.com/r/kcKog5/2 (直接爆栈)

一句非常简短的正则便引发了如此严重的性能问题,以是在写正则时一定要把稳回溯问题,避免利用低效率正则,只管即便避免利用"."这样的正则。

不过,并不是所有场景下该正则都会导致性能问题,这与正则表达式的驱动引擎密切干系,会在后文中先容。

捕获与引用

括号在正则中可以用于捕获,被括号括起来的部分 「子表达式」会被保存成一个捕获组。

捕获,顾名思义,便是指匹配特定的子表达式,并保存结果,如下面的例子:

如图,括号构成的捕获组捕获了两个子表达式,分别是"2023-10-12"和"20:23:32",日期是第一个,韶光是第二个。

但是,如何知道某个子表达式是在哪一个分组呢?很大略,只须要数左括号在哪个位置即可。

日期分组编号是 1,韶光分组编号是 5,年月日对应的分组编号分别是 2,3,4,时分秒的分组编号分别是 6,7,8。

分组也可以进行命名,这里就不展开讲了。

知道编号之后,在大部分情形下,我们就可以通过编号对捕获组进行查找和更换,这便是“引用”,一些引用的常见利用办法如下:

编程措辞

查找时引用办法

更换时引用办法

Python

\number 如 \1

\number 如 \1

Go

官方不支持

官方不支持

Java

\number 如 \1

$number 如 $1

JavaScript

$number 如 $1

$number 如 $1

PHP

\number 如 \1

\number 如 \1

Ruby

\number 如 \1

\number 如 \1

如上例,我们可以用\1代表第一个捕获组,这样在日期被匹配后,\1会匹配一个完备相同的内容。
这种模式很有用,比如在探求两个重复涌现的单词的时候,可以用"(\w+) \1"这一正则来探求,如下所示:

此外,也可以在更换中利用,如下所示:

采取捕获和引用,我们可以更加方便地提取字符串中的一些信息,并进行转换和重组。

这里给出一个大略的例子,利用捕获和引用将11位手机号转为"xxx-xxxx-xxxx"格式:regex101.com/r/7uuu2M/1

不过,由于捕获并引用须要更多的内存,有时,一些不恰当的正则可能会导致性能问题,因而,有些编程措辞不支持引用,如Golang(但是Golang支持捕获),这也与正则表达式的驱动引擎有关,会不才文中详细先容。

匹配模式

正则表达式紧张有四种匹配模式(Match Mode)。
所谓匹配模式,指的是正则中一些 改变元字符匹配行为 的办法,比如匹配时不区分英笔墨母大小写。
常见的匹配模式如下表所示:

匹配模式

描述

不区分大小写模式 (i)

在匹配时不区分大小写。
例如,正则表达式 /hello/i 可以匹配 “hello”、”Hello”、”HELLO” 等。

点号通配模式 (s)

让点号 (.) 也能匹配换行符。
默认情形下,点号只匹配除换行符之外的所有字符。
利用 /hello./ 可以匹配 “hello”、“hello\n\n\n” 等。

多行匹配模式 (m)

在多行模式下,^ 或 不仅仅匹配全体文本的开头或结尾,还可以匹配每一行的开头或结尾。
利用/hello/m不仅仅匹配全体文本的开头或结尾,还可以匹配每一行的开头或结尾。
利用/hello/m 可以匹配 “hello”、“hello\nworld” 中的 “hello”。

注释模式 (x)

注释模式许可在正则表达式中添加注释,以增加可读性。
注释以 # 开头,直到行末结束。

环视

在正则中我们有时候也须要瞻前顾后,找准定位。
环视便是哀求匹配部分的前面或后面要知足(或不知足)某种规则,有些地方也称环视为零宽断言。

环视在检讨子表达式能否匹配的过程中,它们本身不会“占用”任何文本,普通来理解,可以认为表达式在环视进行时,会在前面或后面先检讨是否知足一定的子表达式条件,若知足,则连续匹配。

下面用肯定顺序环视来举例:regex101.com/r/fJoAm5/1

在这个例子中,只有后面的字符串知足"bytedance"时,才会匹配"byte"这一字符串。

环视紧张有四种,下面是对它的简要先容:

类型

正则表达式

匹配成功的条件

示例

肯定逆序环视

(?<=……)

子表达式能够匹配左侧文本

x(?<=abc),能匹配到x,但x后面必须是abc才行

否定逆序环视

(?<!……)

子表达式不能匹配左侧文本

x(?<!abc),能匹配到x,但只有x后面不是abc才行

肯定顺序环视

(?=……)

子表达式能够匹配右侧文本

(?=abc)x,能匹配到x,但x前面必须是abc才行

否定顺序环视

(?!……)

子表达式不能匹配左侧文本

(?!abc)x,能匹配到x,但只有x前面不是abc才行

匹配事理有穷状态自动机

有穷状态自动机(Finite Automaton)是正则表达式处理文本的“引擎”,它根据输入的顺序和当前的状态,逐步改变其状态,在每个状态时都会有一个或多个输出。
有穷状态自动机包含有限个状态,个中一个为初始状态,根据输入的类型,会有一组相应的转换规则决定接下来切换到的状态。

有穷状态自动机的详细实现有NFA(Nondeterministic finite automaton,非确定有限状态自动机)和DFA(Deterministic finite automaton,确定性有限状态自动机)两种,个中,NFA又分为传统NFA和POSIX NFA。

结论先行,这里先给出以上三种引擎的特点总结:

引擎类型

程序

忽略优先量词(非贪婪匹配)

引用

回溯

DFA

Golang、MySQL、awk(大多数版本) 、egrep(大 多数版本) 、flex、lex、Procmail

不支持(Golang除外)

不支持

不支持

传统型NFA

PCRE library、Perl、PHP、Java、Python、Ruby、grep(大多数版本) 、GNU Emacs、less、 more、.NET措辞、sed(大多数版本)、vi

支持

支持

支持

POSIX NFA

mawk、Mortice Kern Systems'utilities、GNU Emacs(明确指定时利用)

不支持

不支持

支持

DFA/NFA稠浊

GNU awk、GNU grep/egrep、Tcl

支持

支持

DFA支持

个中,主流场景下一样平常利用DFA和传统型NFA,下文也紧张针对这二者的特性进行详述。

表达式主导与文本主导

从一个可能的场景出发,来先容NFA和DFA的主要差异:用正则表达式"byte(dance|tech|doc)"来匹配"bytetech"。

NFA引擎:表达式主导

对付NFA来说,正则表达式会从"b"开始,每次检讨一部分(由引擎查看表达式的一部分),同时检讨当前文本是否匹配表达式确当前部分,若是,则连续表达式的下一部分,如此连续,知道表达式的所有部分都能匹配,即全体表达式匹配成功。

在本例子中,正则表达式"byte(dance|tech|doc)" 第一个字母是"b",则它会不断考试测验,直到在目标字符串中找到"b",在找到之后,就检讨紧跟其后的"y"、"t"、"e"能否被匹配,之后进行到多选分枝中,NFA会依次考试测验"dance"、"tech"、"doc"这三种分支,直到匹配成功或报告失落败。
表达式中的掌握权在不同的元素之间转换,以是可以将它称为“表达式主导”。

我们将这一过程转换为NFA图像,如下所示,个中ε代表空转换,意思是不须要任何操作,即可从前一状态转换到下一状态。
我们可以清晰的看到,状态4引出了三个状态,表达式在匹配时,会依次考试测验这三种状态。

注:传统型NFA在多选构造中是从上到下依次选择,而 POSIX NFA则是优先选择较长路径进行匹配。

DFA引擎:文本主导

与NFA不同,DFA引擎在扫描字符串时,会记录“当前有效”的所有匹配可能,还是上面的例子,当引擎移动到"b"时,只有下一个字符是"y",才会移动到下一过程,否则报告匹配失落败。
当引擎移动到"e"时,可能的下一步是"d"或者"t",(可以理解为某个状态须要哪种字符才能转换到下一状态,已经在DFA被构建之时就打算好了),这一步可以表示为:byte(dance|tech|doc),当下一个字母是"t"时,引擎连续向前移动,其他两个分支被淘汰,就这样连续匹配,直到得出结论。

这种办法称为文本主导,是由于它扫描到字符串中的每个字符都对引擎进行了掌握。

笔墨可能不随意马虎精确描述这一过程,我们在这里给出该表达式的DFA转换图,如下所示。
可以看到,在E状态,只有"d"或者"t"会让引擎转换为下一个状态,当走入新的状态时,没有转头路。

在形式理论中可以证明NFA和DFA是等价的,以是DFA也是从NFA转换而来的,转换过程如下,感兴趣的同学可以理解一下(在编译事理中被大量利用)。

再谈回溯

在上文的表格中提到:NFA可以进行回溯,但是 DFA 不可以。
这是为什么呢?

在正则表达式中,基于NFA的实现许可回溯,由于NFA的每个状态对相同的输入,可能有多种转移选择。
如果一条路径没有找到符合的匹配结果,NFA会回溯到上一个节点,探求其余一条可能的转移路径。
这种设计许可正则表达式具有更加丰富的表达性,并确保找到所有的匹配结果。
我们连续拿出上例中的NFA转换图来举例,在4状态,NFA有三种可能选择的路径,而空转换ε,则为回溯的存在供应了空间,在实现中,可以理解为NFA在空转换中插入了锚点(或者理解为存档点?)当某条路不通时,NFA退回到之前的锚点,换条路连续走,直到匹配成功或报告失落败。

而DFA没有此类回溯的能力,由于在DFA中,每个状态对相同的输入只有一个确定的转移结果。
一旦某个状态的转移操作被触发,就无法再回溯到上一个状态。

我们连续用上图举例,在DFA中,不存在空转换,也就没有锚点一说,全体DFA便是一个只能连续向前的火车,没有转头路,这当然会让匹配效率加快,但比较NFA,也损失了一些功能和灵巧性(例如,如上表所示,DFA不支持引用、回溯等功能)。

从匹配事理角度理解“贪婪匹配”和“非贪婪匹配”“贪婪匹配”从实质上讲,便是在进行匹配优先(标准量词都是匹配优先的)“非贪婪匹配”从实质上讲, 便是在进行忽略优先(在"."后加上忽略优先量词"?"即开启忽略优先)

我们还是回到利用".ab"来匹配"1abxxx"这个例子中,看看如何理解匹配优先和忽略优先。

这里给出该正则表达式的NFA图:

从图中可以看出,从0状态到3状态有两条路径,匹配优先便是优先选择下面一条路径,由于要优先匹配".",一旦被匹配,就会一贯在状态1和状态2之间流转,终极匹配完所有字符后,开始回溯,以匹配后面的"ab",而忽略优先则是优前辈行空转换,直接跳到3状态。

值得一提的是,DFA中不存在“匹配优先”和“忽略优先”的观点,由于二者从NFA转为DFA后,都失落去了空转换,无法进行回溯,如下图所示:

但是DFA仍旧是“贪婪匹配”的,对付该例子,DFA会一贯探求最长的以ab为结尾的子串,直到遍历全体字符串。

只管都遍历了全体字符串,但是DFA的韶光繁芜度是线性的,而且由于没有回溯,避免了栈的无序增长,以是在绝大部分场景下,性能要比NFA好很多。

一些优化建议对付繁芜正则,可以提前编译好再调用,否则每次都会构建一次自动机避免利用"."进行匹配,只管即便准确表示匹配范围对付多分支匹配,提取出公共部分将涌现可能性大的子表达式放在左边只在必要的时候利用捕获组若须要分组以降落理解本钱,只管即便利用非捕获组(?:)当心嵌套的子组重复如(.)regex101.com/r/R0087X/1避免不同分支的重复匹配特性与流派概述正则表达式简史正则表达式的历史可以追溯到二十世纪四十年代,神全心理学家Warren McCulloch和Walter Pitts首先提出了一种数学化的办法来描述神经网络。
进一步,在1956年,数学家Stephen Kleene提出了名为“正则凑集(Regular Sets)”的符号。
1960年代,Unix的创始人之一Ken Thompson将正则表达式整合进了他开拓的文本编辑器qed和ed,并在后续将其加强到了grep工具中。
这给Unix和类Unix的系统工具带来了广泛的运用。
由于早期不存在统一的标准,导致了正则表达式在不同措辞和工具中存在一些差异。
为此,1986年POSIX开始将正则表达式进行标准化,并被Unix及类Unix系统广泛接管。
此后,Perl措辞在20世纪八十年代末也引入了正则表达式,功能强大且利用方便,受到了广泛的欢迎。
并在其根本上,Philip Hazel开拓出了Perl兼容的正则表达式解析引擎——PCRE。
进入到21世纪,又涌现了一个新的正则表达式库——RE2。
这是由谷歌开拓的一个开源C++库,它紧张设计用于安全且有效地匹配大型文本。
RE2 是线性韶光匹配的,适用于处理大规模数据。
至今,正则表达式已经成为所有打算机措辞及许多运用领域不可短缺的工具。
POSIX和PCRE以及RE2是当前广泛运用的紧张规范和库。
正则表达式流派

就像前面所说,目前正则表达式紧张有三大流派(Flavor):POSIX 流派、 PCRE 流派以及RE2流派。

POSIX流派

POSIX 规范定义了正则表达式的两种标准:

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

早期 BRE 与 ERE 标准的差异紧张在于,BRE 标准不支持量词问号和加号,也不支持多选分支构造管道符。
BRE 标准在利用花括号,圆括号时要转义才能表示分外含义。
BRE 标准不能知足全场景须要,于是有了 ERE 标准,在利用花括号,圆括号时不须要转义了,还支持了问号、加号 和 多选分支。

我们现在利用的 Linux 发行版,大多都集成了 GNU 套件。
GNU 在实现 POSIX 标准时,做了一定的扩展。
下面是BRE和ERE的一些比拟:

正则表达式特性

BRE标准

ERE标准

点号.、^、$、[...]、[^...]

✔️

✔️

"任意数目"量词

✔️

✔️

+和?量词

✖️(GNU BRE支持)

✔️

区间量词

{min, max}

{min, max}

圆括号分组

(...)

(...)

量词可否限定圆括号分组

✔️

✔️

捕获文本引用

\1到\9

✖️(GNU ERE支持)

多选分支构造

✖️(GNU BRE支持)

✔️

总之,GNU BRE 和 GNU ERE 它们的功能特性并没有太大差异,差异是在于部分语法层面上,紧张是一些字符要不要转义。

POSIX 流派还有一个分外的地方,便是有自己的字符组,叫 POSIX 字符组。
POSIX字符组不同于常规定义的字符组。
详细的清单和解释如下所示:

POSIX字符组

阐明

等价表示

备注

[[:alnum:] ]

数字和字母

[0-9A-Za-z]

比\w少了下划线

[[:alpha:] ]

字母

[A-Za-z]

[[:ascii:] ]

ASCII

[\x00-\x7F]

[[:blank:] ]

空格和制表符

[\t]

[[:cntrl:] ]

掌握字符

[\x00-\x1F\x7F])

[[:digit:] ]

数字

[0-9]

\d

[[:graph:] ]

可见字符

[!-~] [A-Za-z0-9!"#$%&'()+,-./:;<=>? @\]^_{

}~]

[[:lower:] ]

小写字母

[a-z]

[[:upper:] ]

大写字母

[A-Z]

[[:print:] ]

可打印字符

[-~] [[:graph:] ]

比graph多了空格

[[:punct:] ]

标点符号

[!-/:-@[-`{-~]

[[:space:] ]

空缺符号

[\t\n\v\f\r]

[[:xdigit:] ]

16进制数字

[0-9A-Fa-f]

PCRE流派

PCRE,全称Perl Compatible Regular Expressions,是一个开源库,为了最大限度地兼容Perl的正则表达式语法而设计。
PCRE供应了相对POSIX更丰富的特性,例如命名捕获组、向前和向后查找断言、递归匹配等。

PCRE流派是紧张受Perl措辞的影响而形成的,它利用非常灵巧和丰富的语法,可以运用在各种繁芜的文本处理任务中。
PCRE流派的特点因此方便、灵巧性和功能强大为主,语法元素更加丰富,特性更为强大。

比较之下,POSIX流派紧张遵照POSIX的正则表达式标准。
这个标准更关注通用性和跨平台的兼容性,语法较为简洁。

PCRE流派与POSIX流派的差异在于以下几个方面:

功能特性:PCRE流派的正则表达式语法更为丰富,支持更多分外字符和元字符,供应了更灵巧和强大的模式匹配功能。
而POSIX流派的正则表达式语法较为简洁,功能相对有限。
兼容性:PCRE流派的正则表达式语法和功能是基于Perl措辞的,因此与Perl措辞中的正则表达式非常相似,可以很好地兼容和迁移。
而POSIX流派的正则表达式则是基于POSIX标准,更为规范和标准化。
运用范围:PCRE流派的正则表达式被广泛运用于各种编程措辞和工具中,供应了更大的灵巧性和功能扩展性。
而POSIX流派的正则表达式多用于Unix和类Unix系统中的工具,如grep、sed、awk等。
RE2流派

RE2 是 Google 公司开拓的一个用 C++ 实现的正则表达式库。
它是由 Google 的工程师 Russ Cox 所开拓,于2010年发布。
RE2 的设计旨在确保正则表达式操作的韶光和空间繁芜性与输入大小呈线性关系,并且在算法上避免可导致超线性韶光繁芜性的布局。

RE2 的涌现紧张是由于传统的正则表达式库(例如 PCRE )在处理特定的、繁芜的正则表达式时可能会导致严重的性能问题,乃至做事被拖垮。
这紧张是由于这类库常日采取回溯的算法,对付某些繁芜的正则表达式,可能导致指数级别的匹配韶光。

RE2 在这一块做了优化,它紧张基于DFA构建,担保了在所有情形下都具有线性的韶光繁芜性,从而避免了正则表达式处理的“灾害性回溯”,使得其在须要处理大量数据或繁芜模式时有更高的效率。
但RE2也通过分外办法实现了部分 PCRE 的功能,如非贪婪匹配,它并不是纯粹的DFA。

RE2流派特点如下:

高效性能:RE2引擎在匹配和搜索正则表达式时非常高效,其设计目标是供应快速的操作速率。
安全性:RE2引擎利用了一种受限定的正则表达式语法,可以确保匹配的过程在有限韶光内完成,避免了可能涌现的正则表达式回溯(catastrophic backtracking)问题。
RE2基于DFA构建,能够对一些低效率的正则进行化简,很大程度上避免了上文提到的爆栈问题。
简洁性:RE2引擎供应了一种简洁的正则表达式语法,相对付PCRE来说更加大略易懂,但是短缺了一些高等特性,例如捕获组、后向引用以及环视等。
跨平台可移植性:RE2引擎支持多种编程措辞,包括C++ 、Go、Java和Python(Java和Python的自带正则仍旧属于PCRE流派)等,因此具有很好的跨平台可移植性。
实用例子列出Golang中所有函数名、参数列表及返参列表

这里用x模式来写,便于理解,此外,正则表达式很灵巧,精确答案不止一个。

/ (?<=func\s) # 环视,若左边有func,开始匹配 (\w+) # 第一个捕获组,捕获函数名 (?=() # 环视,若后面有括号,开始匹配(终极效果,只匹配左边有func,右边有空格的字符串) \s # 匹配任意空缺字符 ( # 转义,匹配左括号 ([\w\s.,]) # 第二个捕获组,匹配参数列表 ) # 转义,匹配右括号 \s # 匹配任意空缺字符 ([\w\s.,]) # 第三个捕获组,匹配返参列表/gmx

详细匹配过程及示例如下:regex101: build, test, and debug regex

校验密码强度

利用环视进行密码长度校验,会让全体正则更加清晰易懂,但把稳这里用了".",在贪婪匹配下可能有性能问题,以是在校验之前最好先限定一下密码长度。

/ ^ # 匹配行首 (?=.[A-Z]) # 向前环视,匹配到大写字母 (?=.[a-z]) # 向前环视,匹配到小写字母 (?=.\d) # 向前环视,匹配到数字 (?=.[_\W]) # 向前环视,匹配到分外字符 .{10,} # 若知足以上环视条件,且有10个以上字符,则进行匹配 $ # 匹配行尾/gmx

详细匹配过程及示例如下:regex101: build, test, and debug regex

一些措辞不支持环视(如Golang),可以利用多个正则来对各个条件进行分别判断。

剔除Markdown中的代码

这个问题并没有看上去那样大略,由于我们可能对Markdown中插入代码之灵巧性不足理解。

首先,我们须要知道,如何在Markdown中导出代码块,紧张有三种办法:

三个及以上反引号(开始反引号数量三个及以上,结束反引号数量大于即是开始反引号数量)对导出一个代码块

三个及以上波浪号对(开始波浪号数量三个及以上,结束波浪号数量大于即是开始波浪号数量)导出一个代码块

四个及以上空格或一个及以上的制表符导出一个代码块

要全部考虑以上情形,是很繁芜的,考虑到不同措辞和标准对各个特性的支持程度不同,这里给出两个版本的答案:

第一个版本:Golang版,由于Golang属于RE2流派,不支持回溯、引用与环视,以是只用它对三个反引号和三个波浪号导出的代码块进行匹配,这个版本能办理85%的问题。

/ /(?: # 采取非捕获括号进行分组(非必要不该用捕获型括号) ```[\s\S]?``` # 采取非贪婪匹配(必须),匹配0到n次空缺或非空缺字符(任意字符) ) # 结束第一个分支 | # 或符号 (?: # 同上,采取非捕获型括号 ~~~[\s\S]?~~~ # 同上,非贪婪匹配来匹配0到n次空缺或非空缺字符 ) # 结束第二个分支/gmx

详细匹配过程及示例:regex101: build, test, and debug regex

第二个版本, PCRE 版,也便是大部分编程措辞支持的流派,能够回溯、引用与环视,以是可以用它来匹配四个及以上空格或一个及以上制表符导出的代码块(须要采取肯定逆序环视不雅观察前一行,避免误判),这个版本能办理98%的问题。

/ /(?: # 采取非捕获括号进行分组(非必要不该用捕获型括号) (`{3,}) # 捕获组,匹配三个及以上反引号,并进行捕获 [\s\S]? # 采取非贪婪匹配(必须),匹配0到n次空缺或非空缺字符(任意字符) (?:(?:\1`)|\Z) # 引用,引用了之前的捕获组,若之前捕获了5个反引号,则在此必须匹配5个以上反引号,若开头三个以上反引号,后面没有比开头更多的反引号,则后面都是代码块 ) # 结束第一个分支 | # 或符号 (?: # 同上,采取非捕获型括号 (~{3,}) # 同上,匹配三个及以上波浪号并进行捕获 [\s\S]? # 同上,非贪婪匹配来匹配0到n次空缺或非空缺字符 (?:(?:\2~)|\Z) # 引用,引用了之前的捕获组 ) # 结束第二个分支 | # 或符号 (?: # 同上,采取非捕获型括号 (?<= # 肯定型逆序环视,左侧文本必须知足条件才可以匹配 (?:^\n)|(?:^\A) # 上一行必须只有一个回车(以回车符号开头)或没有上一行 ) # 结束环视 (?: # 采取非捕获型括号分组 (?:\s{4,}?|\t+?)[^\n](?:\n|$) # 匹配四个及以上空格或一个以上制表符开启的一行 )+ # 上面的子组匹配1次到n次 ) # 结束第三个分支/gxm

详细的匹配过程及示例:regex101: build, test, and debug regex

但是以上正则表达式都没有考虑最极度的一种情形:在Markdown中的列表之间,须要八个空格或两个制表符才能导出一个代码块,PCRE版本虽然能正常匹配,但存在误判的可能,若考虑这一点,正则表达式将更为繁芜,也不便于阅读调试,故不给出。
上面的例子已经可以适用于绝大部分场景。
实际场景中应该综合考虑本钱与繁芜度来选择正则,正则无法办理所有问题,以是难免存在一些badcase,很多问题须要和程序结合才能得到更好的办理。

一些思考正则表达式的实质是什么?

正则表达式可以看作是一种微型的、专注于文本处理的编程措辞。
正则表达式有自己的语法和构造,可以用来描述和匹配一系列知足某些特性的字符串。
它实质上也是一门编程措辞。
与SQL、LaTeX等措辞类似,都是针对问题领域专门设计的语法元向来编写程序或表达式。

作为一种高度抽象的编程措辞,正则表达式是为特定场景设计的领域特定措辞(DSL),不同于我们一样平常打仗的通用编程措辞(GPPL),它在打算机能力和表达能力上是有很大限定的,但在自己的善于领域——字符串处理领域,拥有极其强大的能量。

但纵然如此,我们也不能奢望,用“一行很酷的正则”办理所有问题,正如Fred Brooks在《人月神话》中提到的,“没有银弹”,每个项目都有其特定的寻衅和限定,须要综合考虑多种成分,并灵巧应对。
软件开拓是一项繁芜的任务,须要结合多种技能、方法和履历,持续演进和改进。

或许,正则表达式的发展进程也在提醒我们把稳这一思想吧,从POSIX到PCRE,再到RE2,表示了技能职员在不同阶段对标准性、功能性、安全性的不同追求,我们在日常开拓时也应把稳随机应变,灵巧应对。

原文链接:https://juejin.cn/post/7294425916548317199

相关文章

介绍直播新纪元,轻松进入直播的五大步骤

随着互联网技术的飞速发展,直播行业在我国逐渐崛起,越来越多的人选择通过直播这一新兴媒介展示自己、分享生活、传递价值。对于许多新手来...

网站推广 2025-01-03 阅读0 评论0

介绍相机美颜原理,科技与美学的完美结合

随着科技的发展,智能手机的摄像头功能日益强大,美颜相机成为了许多人拍照的首选。美颜相机不仅满足了人们对于美的追求,更在视觉上给人带...

网站推广 2025-01-03 阅读0 评论0

介绍磁铁的制造,科学与艺术的完美结合

磁铁,一种神秘的物质,自古以来就吸引了无数人的目光。它不仅具有独特的磁性,还能在工业、医疗、科研等领域发挥重要作用。磁铁是如何制造...

网站推广 2025-01-03 阅读0 评论0

介绍电瓶激活方法,让电池焕发新生

随着科技的不断发展,电动汽车逐渐成为人们出行的首选。而电瓶作为电动汽车的核心部件,其性能直接影响着车辆的续航里程和行驶体验。新购买...

网站推广 2025-01-03 阅读0 评论0