当前位置: 首页 > news >正文

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.dtcms.com/a/327617.html

相关文章:

  • WPS文字和Word:不只是表格,段落也可以排序
  • gpt-5与gpt-5-fast
  • 【新模型速递】PAI-Model Gallery云上一键部署gpt-oss系列模型
  • 一起来聊聊GPT-5
  • c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)详解和代码示例
  • 使用pyqt5实现可勾选的测试用例界面
  • B站 韩顺平 笔记 (Day 16)
  • 如何以开发者的身份开发出比python更好的应用软件?
  • 攻击者将Linux摄像头武器化为攻击工具,可注入击键并发动攻击
  • 使用reqwest+select实现简单网页爬虫
  • 《Fast Automatic White Balancing Method by Color Histogram Stretching》论文笔记
  • 小米宠物空气净化器好用吗?希喂/小米/范罗士核心性能深度对比
  • 5G专网项目外场常见业务测试指南(六)-PingInfoView
  • 力扣面试150(54/150)
  • 如何构建PHP表单页面及验证相关原理(PHP基础)
  • 六十、【Linux系统lvs应用】LVS简介 、 LVS-NAT集群 、 LVS-DR集群
  • 微服务ETCD服务注册和发现
  • 3 Abp 核心框架(Core Framework)
  • 过程设计工具深度解析-软件工程之详细设计(补充篇)
  • 数字孪生如何推动智慧园区精细化管理
  • CV 医学影像分类、分割、目标检测,之【皮肤病分类】项目拆解
  • OHEM (在线难例挖掘) 详细讲解
  • 【Vue.js】生产设备规划工具(报价单Word文档生成)【开发全流程】
  • 无人机航拍数据集|第14期 无人机水体污染目标检测YOLO数据集3000张yolov11/yolov8/yolov5可训练
  • etcd 备份与恢复
  • Etcd客户端工具Etcd Workbench更新了1.2.0版本!多语言支持了中文,新增了许多快捷功能使用体验再次提升
  • Spark 运行流程核心组件(一)作业提交
  • 干货分享|如何从0到1掌握R语言数据分析
  • 小红书笔记信息获取_实在智能RPA源码解读
  • 邦纳BANNER相机视觉加镜头PresencePLUSP4 RICOH FL-CC2514-2M工业相机