5 重复匹配

在前几章里,我们学习了如何使用各种元字符和特殊的字符集合去匹配单个字符。

本章将学习如何匹配多个连续重复出现的字符或字符集合。


5.1 有多少个匹配

你现在已经学会了正则表达式的模式匹配中的基础知识,但目前所有的例子都有一个非常严重的局限。

请大家思考一下,如何构造一个匹配电子邮件地址的正则表达式。电子邮件地址的基本格式如下所示:

text@text.text

利用前一章讨论的元字符,你可能会写出这样的正则表达式:

\w@\w\.\w

​\w​可以匹配所有的大小写字母、数字的字符(以及下划线字符_​,这个字符在电子邮件地址里是有效的),@​字符不需要被转义,但.​字符需要。

这个正则表达式本身没有任何错误,可它几乎没有任何实际的用处。它只能匹配形如a@b.c​的电子邮件地址(虽然在语法方面没有任何问题,但这显然不是一个有效地址)。问题在于\w​只能匹配单个字符,而我们无法预知电子邮件地址的各个字段会有多少个字符。举个最简单的例子,下面这些都是有效的电子邮件地址,但它们在@​前面的字符个数都不一样:

b@forta.com
ben@forta.com
bforta@forta.com

你需要的是,想办法能够匹配多个字符,这可以通过使用几种特殊的元字符来做到。

5.1.1 + :匹配1~∞个字符

要想匹配某个字符(或字符集合)的一次或多次重复,只要简单地在其后面加上一个+​​字符就行了。+​匹配一个或多个字符(至少一个;不匹配零个字符的情况)。比如,a​匹配a​本身,a+​匹配一个或多个连续出现的a​。类似地,[0-9]​匹配任意单个数字,[0-9]+​匹配一个或多个连续的数字。

提示 在给一个字符集合加上+​后缀的时候,必须把+放在这个字符集合的外面。比如说,[0-9]+​是正确的,[0-9+]​则不正确。

​[0-9+]​其实也是一个有效的正则表达式,但它匹配的不是一个或多个数字,它定义了一个由数字0到9和+​构成的字符集合,因而只能匹配单个的数字字符或加号。虽然有效,但它并不是我们需要的东西。

重新回到电子邮件地址的例子,我们这次使用+​来匹配一个或多个字符:

文本

Send personal email to ben@forta.com. 
For questions about a book use support@forta.com. 
Feel free to send unsolicited email to spam@forta.com.
(wouldn't it be nice if it were that simple, huh?).

正则表达式

\w+@\w+\.\w+

结果

Send personal email to ben@forta.com.

For questions about a book use support@forta.com.

Feel free to send unsolicited email to spam@forta.com.

(wouldn't it be nice if it were that simple, huh?).

分析

该模式正确地匹配到了所有的3个电子邮件地址。这个正则表达式先用第一个\w+​匹配一个或多个字母数字字符,再用第二个\w+​匹配@​后面的一个或多个字符,然后匹配一个.​字符(使用转义序列\.​),最后用第三个\w+​匹配电子邮件地址的剩余部分。

提示 +​是一个元字符。如果需要匹配+​本身,就必须使用转义序列\+​。

--PostgreSQL
with t1 as (
select 'Send personal email to ben@forta.com. '  txt 
union all
select 'For questions about a book use support@forta.com. ' 
union all
select 'Feel free to send unsolicited email to spam@forta.com.'  
union all
--两个单引号表示一个单引号,这样不会妨碍其他字符串的正常输入
select '(wouldn''t it be nice if it were that simple, huh?).'  
)
select txt
,regexp_replace(txt,'\w+@\w+\.\w+','XXXXXXX','g') 
from t1 

​+​​还可以用来匹配一个或多个字符集合。为了演示这种用法,我们在下面这个例子里使用了和刚才一样的正则表达式,但文本内容和上一个例子中稍有不同:

文本

Send personal email to ben@forta.com or ben.forta@forta.com. 
For questions about a book use support@forta.com. 
If your message is urgent try ben@urgent.forta.com. 
Feel free to send unsolicited email to spam@forta.com. 
(wouldn't it be nice if it were that simple, huh?)

正则表达式

\w+@\w+\.\w+

结果

Send personal email to ben@forta.com or ben.forta@forta.com.
For questions about a book use support@forta.com.
If your message is urgent try ben@urgent.forta.com.
Feel free to send unsolicited email to spam@forta.com.
(wouldn't it be nice if it were that simple, huh?)

分析

这个正则表达式匹配到了5个电子邮件地址,但其中有2个不够完整。为什么会这样?

因为\w+@\w+\.\w+​并没有考虑到@​之前的.​字符,它只允许@​之后的两个字符串之间出现单个.​字符。尽管ben.forta@forta.com​是一个完全有效的电子邮件地址,但该正则表达式只能匹配forta​(而不是ben.forta​),因为\w​只能匹配字母数字字符,无法匹配出现在字符串中间的.​字符。

--PostgreSQL
with t1 as (
select 'Send personal email to ben@forta.com or ben.forta@forta.com.'  txt 
union all
select 'For questions about a book use support@forta.com.' 
union all
select 'If your message is urgent try ben@urgent.forta.com.'  
union all
select 'Feel free to send unsolicited email to spam@forta.com.'  
union all
select '(wouldn''t it be nice if it were that simple, huh?).'  
)
select txt
,regexp_replace(txt,'\w+@\w+\.\w+','XXXXXXX','g') 
from t1 

在这里,需要匹配\w​或.​。用正则表达式语言来说,就是匹配字符集合[\w.]​。下面是改进版本:

文本

Send personal email to ben@forta.com or ben.forta@forta.com.
For questions about a book use support@forta.com.
If your message is urgent try ben@urgent.forta.com.
Feel free to send unsolicited email to spam@forta.com.
(wouldn't it be nice if it were that simple, huh?)

正则表达式

[\w.]+@[\w.]+\.\w+

结果

Send personal email to ben@forta.com or ben.forta@forta.com.
For questions about a book use support@forta.com.
If your message is urgent try ben@urgent.forta.com.
Feel free to send unsolicited email to spam@forta.com.
(wouldn't it be nice if it were that simple, huh?)

分析

新的正则表达式看起来用了些技巧。[\w.]+​匹配字母数字字符、下划线和.​的一次或多次重复出现,而ben.forta​完全符合这一条件。@​字符之后也用到了[\w.]+​,这样就可以匹配到层级更深的域(或主机)名。

注意 这个正则表达式的最后一部分是\w+​而不是[\w.]+​,你知道这是为什么吗?如果把[\w.]​用作这个模式的最后一部分,在第二、第三和第四个匹配上就会出问题,因为会把该句子末尾的.​也匹配进去。

注意 你可能已经注意到了:我们没有对字符集合[\w.]​里的.​字符进行转义,但依然能够匹配.​字符。一般来说,当在字符集合里使用的时候,像.​和+​这样的元字符将被解释为普通字符,不需要转义,但转义了也没有坏处。[\w.]​的使用效果与[\w\.]​是一样的。

--PostgreSQL
with t1 as (
select 'Send personal email to ben@forta.com or ben.forta@forta.com. '  txt 
union all
select 'For questions about a book use support@forta.com. ' 
union all
select 'If your message is urgent try ben@urgent.forta.com.'  
union all
select 'Feel free to send unsolicited email to spam@forta.com. '  
union all
select '(wouldn''t it be nice if it were that simple, huh?).'  
)
select txt
,regexp_replace(txt,'[\w.]+@[\w.]+\.\w+','XXXXXXX','g') 
from t1 

5.1.2 * :匹配0~∞个字符

​+​匹配一个或多个字符,但不匹配零个字符,+​最少也要匹配一个字符。

那么,如果你想匹配一个可有可无的字符,也就是该字符可以出现零次或多次的情况,该怎么办呢?

这种匹配需要用*​​元字符来完成。*​的用法与+​完全一样,只要把它放在某个字符(或字符集合)的后面,就可以匹配该字符(或字符集合)出现零次或多次的情况。比如说,模式B.* Forta​将匹配B Forta​、B. Forta​、Ben Forta​以及其他组合。

为了演示+​的用法,来看一个修改版的电子邮件地址示例:

文本

Hello .ben@forta.com is my email address.

正则表达式

[\w.]+@[\w.]+\.\w+

结果

Hello .ben@forta.com is my email address.

分析

​[\w.]+​匹配字母数字字符、下划线和.​的一次或多次重复出现,而.ben​完全符合这一条件。这显然是一个打字错误(文本里多了一个.​),不过这无关紧要。更大的问题在于,尽管.​​是电子邮件地址里的有效字符,但把它用作电子邮件地址的第一个字符就无效了。

--PostgreSQL
with t1 as (
select 'Hello .ben@forta.com is my email address.'  txt  
)
select txt
,regexp_replace(txt,'[\w.]+@[\w.]+\.\w+','XXXXXXX','g') 
from t1 

换句话说,你需要匹配的其实是带有可选的额外字符的字母数字文本,就像下面这样:

文本

Hello .ben@forta.com is my email address.

正则表达式

\w+[\w.]*@[\w.]+\.\w+

结果

Hello .ben@forta.com is my email address.

分析

这个模式看起来更难懂了(正则表达式的外表往往比实际看起来复杂),我们一起来看看吧。\w+​​匹配任意单个字母数字字符(这些可以作为电子邮件地址起始的有效字符),但不包括.​。经过开头部分若干个有效字符之后,也许会出现一个.和其他额外的字符,不过也可能没有。[\w.]*​匹配.​​或字母数字字符的零次或多次重复出现,这正是我们所需要的。

注意 可以把*​理解为一种“使其可选”(make it optional)的元字符。+​需要最少匹配一次,而*​可以匹配多次,也可以一次都不匹配。

提示 *​是一个元字符。如果需要匹配*​本身,就必须使用转义序列\*​。

--PostgreSQL
with t1 as (
select 'Hello .ben@forta.com is my email address.'  txt  
)
select txt
,regexp_replace(txt,'\w+[\w.]*@[\w.]+\.\w+','XXXXXXX','g') 
from t1 

5.1.3 ?:匹配0~1个字符

另一个非常有用的元字符是?​。和+​一样,?能够匹配可选文本(所以就算文本没有出现,也可以匹配)。但与+​不同,?​只能匹配某个字符(或字符集合)的零次或一次出现,最多不超过一次。?​非常适合匹配一段文本中某个特定的可选字符。

请看下面这个例子:

文本

The URL is http://www.forta.com/.
It's to connect securely use https://www.forta.com/ instead.

正则表达式

http:\/\/[\w.\/]+[\w\/]

结果

The URL is http://www.forta.com/.
It's to connect securely use https://www.forta.com/ instead.

分析

该模式用来匹配URL地址:http:\/\/​(包含两个转义斜杠,因此匹配普通文本)加上[\w.\/]+​(匹配字母数字字符、.​和/​的一次或多次重复出现)。

这个模式只能匹配第一个URL地址(以http://​开头的那个),不能匹配第二个(以https://​开头的那个)。简单地在http​的后面加上一个s*​(s​的零次或多次重复)并不能真正解决这个问题,因为这样也能匹配httpsssss://​(显然是无效的URL)。

--PostgreSQL
with t1 as (
select 'The URL is http://www.forta.com/.'  txt  
union all
select 'It''s to connect securely use https://www.forta.com/ instead.'  txt  
)
select txt--对于正斜杠/,转不转义,都可以,如http://[\w./]+[\w/]
,regexp_replace(txt,'http:\/\/[\w.\/]+[\w\/]','XXXXXXX','g') 
from t1 

怎么办?可以在http​的后面加上一个s?​,看看下面这个例子:

文本

The URL is http://www.forta.com/.
It's to connect securely use https://www.forta.com/ instead.

正则表达式

https?:\/\/[\w.\/]+[\w\/]

结果

The URL is http://www.forta.com/.
It's to connect securely use https://www.forta.com/ instead.

分析

该模式的开头部分是https?:\/\/​。?​在这里的含义是:前面的字符(s​)要么不出现,要么最多出现一次。换句话说,https?:\/\/​既可以匹配http://​,也可以匹配https://​(仅此而已)。

--PostgreSQL
with t1 as (
select 'The URL is http://www.forta.com/.'  txt  
union all
select 'It''s to connect securely use https://www.forta.com/ instead.'  txt  
)
select txt
,regexp_replace(txt,'https?:\/\/[\w.\/]+[\w\/]','XXXXXXX','g') 
from t1 

​?​还可以顺便解决4.2节里的一个问题。当时我们使用\r\n​匹配行尾标记,而且我还说过,在Unix或Linux系统上得使用\n​(不包括\r​),理想的解决方案是匹配一个可选的\r​和一个\n​。下面还是那个例子,但这次使用的正则表达式略有不同:

文本

"101","Ben","Forta"
"102","Jim","James""103","Roberta","Robertson"
"104","Bob","Bobson"

正则表达式

[\r]?\n[\r]?\n

结果

"101","Ben","Forta"
"102","Jim","James""103","Roberta","Robertson"
"104","Bob","Bobson"

分析

​[\r]?\n​匹配一个可选的\r​和一个必不可少的\n​。

提示 你应该已经注意到了,上面这个例子里的正则表达式使用的是[\r]?​而不是\r?​。[\r]​定义了一个字符集合,该集合只有元字符\r​这一个成员,因而[\r]?​在功能上与\r?​完全等价。[ ]​的常规用法是把多个字符定义为一个集合,但有不少程序员喜欢把一个字符也定义为一个集合。这么做的好处是可以增加可读性和避免产生误解,让人们一眼就可以看出随后的元字符应用于谁。如果你打算同时使用[ ]​和?​,记得把?放在字符集合的外面。因此,http[s]?://​是正确的,若是写成http[s?]://​可就不对了。

提示 ?​是一个元字符。如果需要匹配?​本身,就必须使用转义序列\?​。

--PostgreSQL
with t1 as (
select 
'"101","Ben","Forta"
"102","Jim","James""103","Roberta","Robertson"
"104","Bob","Bobson"'  txt
--一般windows系统下,文本类型中,每行以\r\n结尾;
--linux下,文本中,每行一般以\n结尾。
--这个字符串,因为是从windows系统上提交给服务器的,所以文本中每行以\r\n结尾
)
select txt--E'\r\n'的写法,表示我是用回车加换行符,去做替代,而非'\r\n'这四个字符,regexp_replace(txt,'[\r]?\n[\r]?\n',E'\r\n','g')     r1 
from t1


5.2 匹配的重复次数

正则表达式里的+​​、*​​和?​​解决了许多问题,但有时候光靠它们还不够。请思考以下问题:

​+​、*​和?​匹配的字符,最小数量是零个或一个。我们无法明确地为其匹配的字符个数另行设定一个最小值。

​+​和*​匹配的字符个数,最大数量是没有上限。我们无法为其匹配的字符个数设定一个最大值。

总而言之,我们无法指定具体的匹配次数。

为了解决这些问题并对重复性匹配有更多的控制权,正则表达式允许使用重复范围(interval)。重复范围在{​​和}​​之间指定。

注意 {​和}​是元字符。如果需要匹配自身,就应该用\​对其进行转义。

值得一提的是,即使你没有对{​和}​进行转义,大部分正则表达式实现也能正确地处理它们(根据具体情况把它们解释为普通字符或元字符)。话虽如此,为了避免不必要的麻烦,最好不要依赖这种行为。在需要把{和}当作普通字符来匹配的场合,应该对其进行转义。

5.2.1 具体的重复匹配

要想设置具体的匹配次数,把数字写在{​​和}​​之间即可。比如说,{3}​意味着匹配前一个字符(或字符集合)3次。如果只能匹配2次,则不算是匹配成功。

为了演示这种用法,我们再来看一下匹配RGB值的例子(请对照第3章和第4章里的类似例子)。你应该记得,RGB值是一个十六进制数值,这个值分成3个部分,每个部分包括两位十六进制数字。下面是我们在第3章里用来匹配RGB值的模式:

#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]

下面是我们在第4章里用来匹配RGB值的模式,它使用了POSIX字符类:

#[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]

这两个模式的问题在于,你不得不重复写出6次相同的字符集合(或POSIX字符类)。下面是一个同样的例子,但我们这次使用了区间匹配:

文本

div {background-color: #fefbd8;
}
h1 {background-color: #0000ff;
}
div {background-color: #d0f4e6;
}
span {background-color: #f08970;
}

正则表达式

#[A-Fa-f0-9]{6}

结果

div {
background-color: #fefbd8;
}
h1 {
background-color: #0000ff;
}
div {
background-color: #d0f4e6;
}
span {
background-color: #f08970;
}

分析

​[A-Fa-f0-9]​匹配单个十六进制字符,{6}​要求重复匹配该字符6次。区间匹配的用法也适用于POSIX字符类。

--PostgreSQL
with t1 as (
select 
'div {background-color: #fefbd8;
}
h1 {background-color: #0000ff;
}
div {background-color: #d0f4e6;
}
span {background-color: #f08970;
}'  txt
)
select txt--使用[[:xdigit:]]代替[a-fA-F0-9],效果一样,regexp_replace(txt,'#[a-fA-F0-9]{6}','XXXXXXX','g')  r1 
from t1

5.2.2 重复匹配的次数范围

​{}​​语法还可以用来为重复匹配次数设定一个区间范围,也就是匹配的最小次数和最大次数。区间必须以{2,4}​(最少重复2次,最多重复4次)这样的形式给出。在下面的例子里,我们将使用一个这样的正则表达式来检查日期的格式:

文本

4/8/17
10-6-2018
2/2/2
01-01-01

正则表达式

\d{1,2}[-\/]\d{1,2}[-\/]\d{2,4}

结果

4/8/17
10-6-2018
2/2/2
01-01-01

分析

这里列出的日期是一些由用户可能通过表单字段输入的值,这些值必须先进行验证,确保格式正确。

​\d{1,2}​​匹配一个或两个数字字符(匹配天数和月份);

​\d{2,4}​​匹配年份;[-\/]​(请注意,这个\/​其实是一个\​和一个/​)匹配日期分隔符-​或/​。

我们总共匹配到了3个日期值,但2/2/2​不在此列(因为它的年份太短了)。

提示 在这个例子里,我们使用了/​的转义序列\/​。这在许多正则表达式实现里是不必要的,但有些正则表达式解析器要求必须这样做。为避免不必要的麻烦,在需要匹配/​字符本身的时候,最好总是使用它的转义序列。

注意,上面这个例子里的模式并不能验证日期的有效性,诸如54/67/9999​之类的无效日期也能通过这一测试。它只能用来检查日期值的格式是否正确(这一环节通常安排在日期有效性验证之前)。

注意 重复范围也可以从0开始。比如,{0,3}​表示重复次数可以是0、1、2或3。我们曾经讲过,?​​匹配它之前某个字符(或字符集合)的零次或一次出现。因此,从效果上看,其等价于{0,1}​。

--PostgreSQL
with t1 as (
select '4/8/17'    txt union all
select '10-6-2018' txt union all
select '2/2/2'     txt union all
select '01-01-01'  txt 
)
select * from t1 
where txt ~ '\d{1,2}[-\/]\d{1,2}[-\/]\d{2,4}'

5.2.3 匹配“至少重复多少次”

重复范围的最后一种用法是指定至少要匹配多少次(不指定最大匹配次数)。这种用法的语法类似于区间范围语法,只是省略了最大值部分而已。比如说,{3,}​表示至少重复3次,换句话说,就是“重复3次或更多次”。

来看一个综合了本章主要知识点的例子。在这个例子里,我们使用一个正则表达式把所有金额大于或等于100美元的订单找出来:

文本

1001: $496.80
1002: $1290.69
1003: $26.43
1004: $613.42
1005: $7.61
1006: $414.90
1007: $25.00

正则表达式

\d+: \$\d{3,}\.\d{2}

结果

1001: $496.80
1002: $1290.69
1003: $26.43
1004: $613.42
1005: $7.61
1006: $414.90
1007: $25.00

分析

这个例子里的文本,取自一份报表,其中第一列是订单号,第二列是订单金额。我们构造的正则表达式首先使用\d+:​​来匹配订单号(这部分其实可以省略——我们可以只匹配金额部分而不是包括订单号在内的一整行)。接着,:​​冒号后面有一个空格不要忽略。

模式\$\d{3,}\.\d{2}​用来匹配金额部分,其中\$​​匹配$​,\d{3,}​匹配至少3位数字(因此,最少也得是100美元),\.​​匹配.​,\d{2}​​匹配小数点后面的2位数字。该模式从所有订单中正确地匹配到了4个符合要求的订单。

提示 在使用重复范围的时候一定要小心。如果你遗漏了花括号里的逗号,那么模式的含义将从至少匹配n​次变成只匹配n​次。

注意 +​在功能上等价于{1,}​。

--PostgreSQL
with t1 as (
select '1001: $496.80'  txt union all
select '1002: $1290.69' txt union all
select '1003: $26.43'   txt union all
select '1004: $613.42'  txt union all
select '1005: $7.61'    txt union all
select '1006: $414.90'  txt union all
select '1007: $25.00'   txt 
)
select * from t1 
where txt ~ '\d+: \$\d{3,}\.\d{2}'


5.3 防止过度匹配

​?​​字符的匹配范围有限(仅限零次或一次匹配),{n}​​和{m,n}​​字符,可以精确重复匹配次数的数量、范围。但除此之外,本章前文中所介绍的其他重复匹配形式,在重复次数方面却没有上限值,而这样做有时会导致过度匹配的现象。

我们目前为止选用的例子都经过了精心挑选,不存在过度匹配的问题。考虑下面这个例子,例子中的文本取自某个Web页面,里面包含两个HTML的标签。我们的任务是用正则表达式匹配标签中的文本(可能是为了替换格式)。

文本

This offer is not available to customers living in <B>AK</B> and <b>HI</b>.

正则表达式

<[Bb]>.*<\/[Bb]>

结果

This offer is not available to customers living in <B>AK</B> and <b>HI</b>.

分析

​<[Bb]>​匹配起始标签(大小写均可),<\/[Bb]>​匹配闭合标签(也是大小写均可)。但这个模式只找到了一个匹配,而不是预期的两个。

第一个标签<B>​​和最后一个标签<B>​​之间的所有内容(AK</B> and <B>HI​)都被.*​​一网打尽。这样做,包含了我们想要匹配的文本,但其中也夹杂了其他标签。

为什么会这样?因为*​​和+​​都是所谓的“贪婪型”(greedy)元字符,其匹配行为是多多益善而不是适可而止。它们会尽可能地从一段文本的开头一直匹配到末尾,而不是碰到第一个匹配时就停止。这是有意设计的,量词1就是贪婪的。

1 +​、*​和?​也叫作“量词”(quantifier)。 ——译者注

--PostgreSQL
with t1 as (
select 'This offer is not available to customers living in <B>AK</B> and <b>HI</b>.'  txt 
)
select txt --对于正斜杠,转不转义,都可以,regexp_replace(txt,'<[Bb]>.*</[Bb]>',  'XXXXXXX','g')   r1 ,regexp_replace(txt,'<[Bb]>.*<\/[Bb]>' ,'XXXXXXX','g')   r2 
from t1; 

在不需要这种“贪婪行为”的时候该怎么办?答案是使用这些量词的“懒惰型”(lazy)版本(之所以称之为“懒惰型”,是因为其匹配尽可能少的字符,而非尽可能多地去匹配)。懒惰型量词的写法,是在贪婪型量词后面加上一个?​。表5-1列出了贪婪型量词及其对应的懒惰型版本。

​*?​是*​的懒惰型版本。下面是使用*?来解决之前那个例子的做法:

文本

This offer is not available to customers living in <B>AK</B> and <b>HI</b>.

正则表达式

<[Bb]>.*?<\/[Bb]>

结果

This offer is not available to customers living in <B>AK</B> and <b>HI</b>.

分析

问题解决了。因为使用了懒惰型的*?​,第一个匹配将仅限于AK​,HI​则成为了第二个匹配。

注意 为了让模式尽可能简单,本书里的大多数例子使用的都是“贪婪型”量词。但是,可以根据需要将其替换成“懒惰型”量词。

--PostgreSQL
with t1 as (
select 'This offer is not available to customers living in <B>AK</B> and <b>HI</b>.'  txt 
)
select txt --对于正斜杠,转不转义,都可以,regexp_replace(txt,'<[Bb]>.*?</[Bb]>','XXXXXXX','g')   r1 ,regexp_replace(txt,'<[Bb]>.*?<\/[Bb]>','XXXXXXX','g')  r2 
from t1; 


5.4 小结

在使用重复匹配时,正则表达式的真正威力就显现出来了。

本章介绍了+​​(匹配字符或字符集合的一次或多次重复出现)、*​​(匹配字符或字符集合的零次或多次重复出现)和?​​(匹配字符或字符集合的零次或一次出现)的用法。

要想获得更大的控制权,你可以使用重复范围{}​​字符,精确地设定重复次数或是重复的最小次数和最大次数。量词分“贪婪型”和“懒惰型”两种,前者会尽可能多地匹配,后者则会尽可能少地匹配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95322.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95322.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【浏览器兼容性处理】

浏览器兼容性处理是前端开发中重要的一环&#xff0c;指解决不同浏览器&#xff08;或同一浏览器不同版本&#xff09;对HTML、CSS、JavaScript解析执行存在差异&#xff0c;导致页面显示异常或功能失效的问题。以下是常见问题及系统的处理方案&#xff1a; 一、常见兼容性问题…

Android组件化实现方案深度分析

组件化是解决大型应用代码臃肿、耦合严重、编译缓慢、团队协作困难等问题的关键架构手段&#xff0c;其核心在于 模块化拆分、解耦、独立开发和按需集成。 一、 组件化的核心目标与价值 解耦与高内聚&#xff1a; 将庞大单体应用拆分为功能独立、职责单一的模块&#xff08;组件…

外卖:重构餐饮的线上服务密码

外卖不是 “把堂食菜装进盒子送出去”&#xff0c;而是 “用线上化服务重构餐饮与用户连接” 的经营模式 —— 它的核心&#xff0c;是 “让用户在家也能吃到‘像在店里一样好’的体验”。一、外卖的底层逻辑用户点外卖&#xff0c;本质是 “想在家获得‘餐厅级体验’”&#x…

C++——高性能组件

文章目录一、什么是高性能组件1.1 C 中高性能组件的核心设计原则1.2 常见的 C 高性能组件 / 库举例1.3 实现高性能组件的关键工具二、定时器2.1 什么是用户态定时器2.2 为什么要使用用户态定时器2.3 高性能用户态定时器的实现原理2.3.1 训练营2.3.1.1 问题解析2.3.1.2 模拟问答…

【软考中级网络工程师】知识点之 UDP 协议:网络通信中的高效轻骑兵

目录一、UDP 协议简介二、UDP 协议特点2.1 无连接性2.2 不可靠性2.3 面向数据报2.4 低开销2.5 广播支持三、UDP 协议工作原理3.1 UDP 报文格式3.2 UDP 数据传输过程四、UDP 协议应用场景4.1 实时音视频传输4.2 在线游戏4.3 DNS 查询4.4 其他应用场景五、UDP 与 TCP 对比5.1 可靠…

【Node.js从 0 到 1:入门实战与项目驱动】2.1 安装 Node.js 与 npm(Windows/macOS/Linux 系统的安装步骤)

文章目录 第 2 章:环境搭建 —— 准备你的开发工具 2.1 安装 Node.js 与 npm(Windows/macOS/Linux 系统的安装步骤) 一、通用安装前检查 二、Windows 系统安装步骤 方法 1:通过官方安装包(推荐) 方法 2:通过 nvm-windows 管理多版本(进阶) 三、macOS 系统安装步骤 方法…

C语言相关简单数据结构:数据结构概念

目录 1.需要的储备知识 2.数据结构相关概念 2.1 什么是数据结构 什么是数据&#xff1f; 什么是结构&#xff1f; 概念&#xff1a; 总结&#xff1a; 2.2 为什么需要数据结构&#xff1f; 结论&#xff1a; C语⾔语法基础到数据结构与算法&#xff0c;前⾯已经掌握并…

Docker 详细介绍及使用方法

Docker 详细介绍及使用方法 一、Docker 是什么&#xff1f; Docker 是一种开源的应用容器引擎&#xff0c;基于 Go 语言开发并遵从 Apache 2.0 协议开源。它允许开发者将应用程序及其依赖打包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上。Dock…

PHP request文件封装

1.继承FormRequest 其中id是路由传参 name是对象中必填校验<?phpnamespace App\Http\Requests\Admin\User;use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule;class user_info_uptRequest extends FormRequest {public function authorize():…

基于跨平台的svg组件编写一个svg编辑器

duxapp 提供了一套跨平台的 SVG 编辑器组件&#xff0c;支持在多种环境中创建和编辑 SVG 图形。该编辑器包含以下核心功能&#xff1a; 插入图片绘制自由路径添加文本创建基本形状&#xff08;矩形、圆形、线条等&#xff09;对元素进行移动、缩放和旋转操作 快速开始 import…

react+echarts实现图表展示的两种方法

前言&#xff1a;reactecharts实现图表展示。1、直接用echarts的插件来实现1&#xff09;安装npm install echarts2&#xff09;使用1、useEffect是react中集合onload/watch监听等方法与一体的hook函数&#xff0c;他的第二个参数是空数组&#xff0c;则等同于onload&#xff0…

Apache 服务器基础配置与虚拟主机部署

Apache 服务器基础配置与虚拟主机部署 Apache 的核心定位与作用&#xff1a; Apache 的核心功能是处理 HTTP 请求并提供 Web 资源&#xff0c;是客户端&#xff08;如浏览器&#xff09;与 Web 服务器之间的 “中间人”&#xff1a; 接收客户端通过 HTTP/HTTPS 协议发送的请求…

线性代数 · 矩阵 | 最小多项式

注&#xff1a;本文为 “矩阵 | 最小多项式” 相关合辑。 略作重排&#xff0c;如有内容异常&#xff0c;请看原文。 最小多项式 橘子蜂蜜 于 2019-05-22 22:48:25 发布 根据哈密顿 - 凯莱&#xff08;Hamilton - Cayley&#xff09;定理&#xff0c;任给数域 PPP 上的一个 …

docter的使用、vscode(cursor)和docker的连接,详细分析说明

目录 一、基本命令 二、用案例来学习使用方法 &#x1f680; Pull Python 3.11 镜像并创建命名容器 &#x1f4cb; 其他有用命令 在容器中安装依赖 三、直接在镜像中安装依赖&#xff08;创建自己定制的镜像&#xff09; 四、在 cursor 中选用容器作为编译器 五、对于整…

如何使用AI大语言模型解决生活中的实际小事情?

我们总以为AI是遥不可及的未来科技&#xff0c;却忽视了它早已成为生活中最实用的“隐形助手”。在信息爆炸的今天&#xff0c;我们每天被无数生活琐事包围&#xff1a;一封专业邮件反复修改措辞、孩子突如其来的数学难题、冰箱里仅剩的食材如何搭配、旅行行程的繁琐规划……这…

关于微信小程序的笔记

1.需要获取demo素材图片方法&#xff08;2,3&#xff09;2.使用逆向工具进行解包没有安装node的需要安装一下安装npm i -g wedecode0.8.0-beta.3获取小程序文件存放路径/Users/lin/Library/Containers/com.tencent.xinWeChat/Data/.wxapplet/packages/wx060ecb4f74eac0da根据具…

课堂笔记:吴恩达的AI课(AI FOR EVERYONE)-W2 AI项目工作流程

课堂笔记&#xff1a;吴恩达的AI课&#xff08;AI FOR EVERYONE&#xff09;-W2 AI项目工作流程 一、如何开始一个AI项目&#xff1f; 1、建设项目工作流程 2、选择合适的AI项目 3、为这个项目收集数据和组织团队二、AI项目的工作流程 &#xff08;1&#xff09;机器学习项目的…

逐际动力开源运控 tron1-rl-isaacgym 解读与改进

文章目录概览基础框架解读线速度估计观测结构仿真实验点足式步态设计步态相位与接触状态建模步态接触奖励动作延迟我的改进Point-goal Locomotion观测修改奖励修改预训练地形编码器Sliced Wasserstein AutoEncoder模型训练与结果参考材料概览 这篇博客记录了我参加逐际动力创学…

人工智能-python-机器学习-线性回归与梯度下降:理论与实践

文章目录线性回归与梯度下降&#xff1a;理论与实践1. 引言2. 回归分析2.1 什么是回归&#xff1f;2.2 线性回归2.3 损失函数2.4 多参数回归3. 参数求解&#xff1a;最小二乘法3.1 最小二乘法 MSE3.2 最小二乘法的优缺点优点&#xff1a;缺点&#xff1a;4. 梯度下降4.1 梯度下…

前端,elment-plus组件:表格,分页,对话框,表单

Element Plus 核心特性组件体系&#xff1a;表单、表格、弹窗、导航等高频组件设计理念主题定制&#xff1a;Sass 变量覆盖与暗黑模式无缝切换国际化支持&#xff1a;多语言动态切换的实现机制TypeScript 支持&#xff1a;完整的类型定义与开发友好性快速上手指南安装与基础配置…