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
英文地址,請注明出處:http://en.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;完整的類型定義與開發友好性快速上手指南安裝與基礎配置…