柚子快報(bào)邀請碼778899分享:正則表達(dá)式
柚子快報(bào)邀請碼778899分享:正則表達(dá)式
正則表達(dá)式(Regular Expression)也叫匹配模式(Pattern),用來檢驗(yàn)字符串是否滿足特 定規(guī)則,或從字符串中捕獲滿足特定規(guī)則的子串。
字符匹配
最簡單的正則表達(dá)式由“普通字符”和“通配符”組成。比如“Room\d\d\d”就這樣 的正則表達(dá)式。 這些位置是普通字符 R o o m \d \d \d 20 這 3 個(gè)位置各匹配一個(gè)數(shù)字 其中“Room”是普通字符,而“\d”是通配符,表示該位置上有一個(gè)數(shù)字。該表達(dá)式 共占用了 7 個(gè)位置,第一個(gè)位置上是字母“R”,第二個(gè)位置和第三個(gè)位置上都是字母“o”, 第四個(gè)位置上是字母“m”,而第五到第七個(gè)位置上是三個(gè)數(shù)字。所以正則表達(dá)式 “Room\d\d\d”代表著“以Room開頭,以三個(gè)數(shù)字結(jié)尾”的那一類字符串。比如字符串 “Room101”、“Room415”、“Room888”等都與“Room\d\d\d”匹配① 。
用于正則表達(dá)式的通配符如下 所示,每個(gè)通配符都代表一類字符
?
.NET 提供了一批與正則表達(dá)式相關(guān)的類,它們都位于 System.Text.RegularExpressions 命名空間,現(xiàn)在我們來學(xué)習(xí)最主要的 Regex 類,并用它來匹配正則表達(dá)式。 Regex 類的部分方法如表 20-2 所示?
現(xiàn)在假設(shè)某份文件里包含著 Kitty 的房間號(hào)碼(格式為 RoomXXX),檔案很長,人 工查閱很耗時(shí)間,那么如何通過計(jì)算機(jī)幫我們找到房間號(hào)碼呢??
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
//注意在文件頭引用命名空間 System.Text.RegularExpressions。
namespace Pattern202405
{
internal class Program
{
static void Main(string[] args)
{
string text = "The Colony is a beautiful town...Kitty lives in Room415...";
//正則表達(dá)式
Regex expression = new Regex(@"Room\d\d\d");
//找出所有匹配的字符串,放入集合中
MatchCollection matches = expression.Matches(text);
//輸出匹配的字符串
foreach (Match match in matches)
{
Console.WriteLine("She lives in {0}", match);
Console.ReadKey();
}
}
}
}
---------------------------------------?
可選字符集
除了通配符外,我們還可以把某個(gè)位置上允許出現(xiàn)的字符寫在方括號(hào)[]內(nèi),組成可選字符集,比如?
[abc]表示該位置可以出現(xiàn)字母 a、b、c? [A-D]表示該位置可以出現(xiàn)字母 A、B、C、D? [A-DM-P]表示該位置可以出現(xiàn)字母 A 到 D 或 M 到 P? [A-Da-d]表示該位置可以出現(xiàn)字母 A 到 D 或 a 到 d? [12] 表示該位置可以出現(xiàn)數(shù)字 1 或數(shù)字 2? [1-5]表示該位置可以出現(xiàn)數(shù)字 1 到 5? [0-57-9]表示該位置可以出現(xiàn)除 6 外的所有數(shù)字 [\s\S]表示該位置可以出現(xiàn)任何字符,包括任何可見字符和不可見字符(如空格、制表符、換行等)
如果想從字符串中找出名字以 V 或 R 開頭,出生于 80~90 年代的男孩,可以用下面 的正則表達(dá)式: [VR] [a-z] [a-z] [a-z] [a-z] - 1 9 [89] [0-9]?
它匹配的字符串包含 10 個(gè)字符,第 1 個(gè)字符為“V 或 R”,第 2~5 個(gè)字符為“a 到 z 的某個(gè)小寫字母”,第 6~8 個(gè)字符為“-19”,第 9 個(gè)字符為“數(shù)字 8 或 9”,第 10 個(gè)字 符為“0 到 9 之間的某個(gè)數(shù)字”。 注意,不管中括號(hào)看起來有多長,它都只代表一個(gè)字符。表達(dá)式中的[VR]、[a-z]、[89]、 [0-9]都是一個(gè)字符,不能看作多個(gè)字符。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Pattern202405_2
{
internal class Program
{
static void Main(string[] args)
{
string boys = "Vitor-1979 Verne-1982 Regan-1998 Robin-2008";
//string text = "Vitor-1970 Verne-1982 Regan-1998 Robin2008";
//正則表達(dá)式
Regex expression = new Regex(@"[VR][a-z][a-z][a-z][a-z]-19[89][0-9]");
//獲取并輸出匹配的字符串
foreach (Match match in expression.Matches(boys))
{
Console.WriteLine(match);
}
Console.ReadKey();
}
}
}
?啟動(dòng)程序:
?反向字符集
?在中括號(hào)表達(dá)式中用符號(hào)“^”表示“非”。比如
[^x] 匹配除 x 以外的所有字符 [^abc] 匹配除 a、b、c 以外的所有字符 [^0-9] 匹配除 0 到 9 以外的所有字符 [^#] 匹配除#以外的所有字符 [^\n] 匹配除換行符\n 以外的所有字符
string sentence = "bog dog fog hog log";?//正則表達(dá)式Regex expression = new Regex(@"[^bd]og");?//輸出匹配的字符串foreach (Match match in expression.Matches(sentence ))?{??Console.WriteLine(match);?}
正則表達(dá)式“[^bd]og”匹配的字符串包含三個(gè)字符,第一個(gè)字符 是除 b 和 d 以外的字符,后兩個(gè)字符為 og?
或匹配
在正則表達(dá)式中用符號(hào)“|”表示“或”。比如 "x|y" 匹配 "x"或"y"? "good|ok" 匹配 "good"或"ok"? "(tr|b)ee" 匹配 "tree"或"bee"? "th(i|a)nk" 匹配 "think"或"thank"? "Book One|Two" 匹配 "Book One"或"Two"? "Book (One|Two)" 匹配 "Book One"或"Book Two"?
數(shù)量限定符
假設(shè)我們要從一串人名中找出以 V 開頭的人名,因?yàn)槿嗣拈L度不是固定的,所以用 一一對應(yīng)的方法就無法實(shí)現(xiàn),這時(shí)需要使用數(shù)量限定符
數(shù)量限定符“*”將前面的字符重復(fù) 0 次或多次。
string words = "lg log loog looog loooog looooog";?//正則表達(dá)式Regex expression = new Regex("lo*g");?//獲取并輸出匹配的字符串foreach (Match match in expression.Matches(words))?{??Console.WriteLine(match);?}? 正則表達(dá)式“l(fā)o*g”表示字符串的第一個(gè)字符為 l,最后一個(gè)字符為 g,中間有零個(gè)或 多個(gè)字母 o。運(yùn)行結(jié)果如圖 所示
數(shù)量限定符“+”將前面的字符重復(fù) 1 次或多次?//正則表達(dá)式 Regex expression = new Regex("lo+g");?
數(shù)量限定符“?”將前面的字符重復(fù) 0 次或 1 次。?//正則表達(dá)式 Regex expression = new Regex("lo?g");
數(shù)量限定符{n,}表示把它前面的字符至少重復(fù) n 遍//正則表達(dá)式 Regex expression = new Regex("lo{3}g"); 正則表達(dá)式“l(fā)o{3}g”表示字符串的第一個(gè)字符為 l,最 后一個(gè)字符為 g,中間有 3 個(gè)字母 o。
數(shù)量限定符{n,m}把前面的字符重復(fù) n 至 m 遍//正則表達(dá)式 Regex expression = new Regex("lo{2,4}g"); 正則表達(dá)式“l(fā)o{2,4}g”表示字符串的第一個(gè)字符為 l,最后一個(gè)字符為 g,中間有 2 到 4 個(gè)字母 o。
正則表達(dá)式“l(fā)(eo)+g”表示字符串的第一個(gè)字符為 l,最后一個(gè)字符為 g,中間有一個(gè) 或多個(gè) eo。
?最奇妙的是這些數(shù)量限定符還可以與通配符組合。比如通配符“\w”匹配任意單詞字 符,限定符“*”表示把前面的字符重復(fù) 0 次或多次,所以正則表達(dá)式“\w*”匹配由任意 單詞字符組成的任意長度的字符串。
string girls = @"Van is 16; Vicky is 18; Vivien is 19; Vinvcent is 22";??Regex expression = new Regex(@"V\w* is \d\d");??foreach (Match match in expression.Matches(girls))??{??Console.WriteLine(match);??}?
貪婪和懶惰?的限定符
“貪婪的”(Greedy),它們會(huì)匹配盡可能多的文本。如果在限定符后 加上?號(hào),它就會(huì)變成“懶惰的”(Lazy),會(huì)匹配盡可能少的文本。
限定符“<.*>”和“<.*?>”都表示以“<”開頭,以“>”結(jié)尾,中間是若干個(gè)字符的字符串,
貪婪限定符盡量找到一個(gè)最長(保留中間可能的匹配)的結(jié)果,而懶惰限定符盡量找最短的結(jié) 果(忽略中間可能的匹配)。?在匹配以特定字符為首尾的字符串時(shí),懶惰的限定符往往能輕松的達(dá)到目的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace 貪婪和懶惰
{
internal class Program
{
static void Main(string[] args)
{
string words = "ab
Hello World
c";//貪婪的限定符
Regex expression1 = new Regex("<.*>");
MatchCollection matchs1 = expression1.Matches(words);
Console.WriteLine("There is {0} match with greedy quantifier:", matchs1.Count);
foreach (Match match in matchs1)
{
Console.WriteLine(match);
}
//懶惰的限定符
Regex expression2 = new Regex("<.*?>");
MatchCollection matchs2 = expression2.Matches(words);
Console.WriteLine("There are {0} matchs with lazy quantifier:", matchs2.Count);
foreach (Match match in matchs2)
{
Console.WriteLine(match);
}
Console.ReadKey();
}
}
}
?懶惰限定符,有兩個(gè)匹配模式值得注意。一個(gè)是“{n}?”,它實(shí)際上與“{n}”是 等價(jià)的,因?yàn)椴还苁秦澙愤€是懶惰,都已經(jīng)限定死了,只能重復(fù) n 次。
另一個(gè)匹配模式是“??”,我們知道“?”表示把前面的字符重復(fù) 0 到 1 次,而“??”又是什么意思呢?
string sentence = "Where bee is, there is honey.";??//用?限定?Regex expression1 = new Regex("be?");??foreach (Match match in expression1.Matches(sentence))??{??Console.WriteLine("be? = {0}", match);??}??//用??限定?Regex expression2 = new Regex("be??");??foreach (Match match in expression2.Matches(sentence))??{??Console.WriteLine("be?? = {0}", match);??}?
運(yùn)行結(jié)果如圖 ?
在正則表達(dá)式“be?”中,“?”并不是把整個(gè)單詞 be 重復(fù) 0 到 1 次,而 是把“?”前面字母“e”重復(fù) 0 到 1 次。因?yàn)椤癰e?”是貪婪的,能重復(fù) 1 次就不重復(fù) 0 次,所以結(jié)果中 e 出現(xiàn)了 1 次;
?而“be??”是懶惰的,重復(fù)次數(shù)要盡量少,所 以它選擇把 e 重復(fù) 0 次
所有限定符都可以和“?”組合?
定位符?
通過定位符可以在指定位置尋找匹配的子串。 若正則表達(dá)式中使用了定位符“^”,則在整個(gè)字符串的頭部尋找匹配的子串。
string words = "year1998 year2008 year2018";?//正則表達(dá)式Regex expression = new Regex(@"^year\d\d\d\d");?//獲取并輸出匹配的字符串foreach (Match match in expression.Matches(words))?{??Console.WriteLine(match);?}
“^”匹配字符串的開頭位置,所以“^year\d\d\d\d”表示要從字符串的開頭位置開始尋 找。運(yùn)行結(jié)果如圖
若正則表達(dá)式中使用了定位符“$”,則在整個(gè)字符串的尾部尋找匹配的子串 //正則表達(dá)式 Regex expression = new Regex(@"year\d\d\d\d$"); “$”匹配字符串的結(jié)尾位置,所以“year\d\d\d\d$”表示匹配的子串緊鄰字符串的結(jié)尾 位置。運(yùn)行結(jié)果如圖 所示
若正則表達(dá)式中使用了定位符“\b”,則在字符串中每個(gè)“單詞”的邊界①尋找匹配 的子串。?
?string words = "formfordfork";??Console.Write("None anchors:");??Regex expression = new Regex(@"for\w");??foreach (Match match in expression.Matches(words))??{??Console.Write(match + "\t");??}?Console.Write("\nAnchor start:");??expression = new Regex(@"\bfor\w");??foreach (Match match in expression.Matches(words))??{??Console.Write(match + "\t");??}??Console.Write("\n Anchor end:");? ?expression = new Regex(@"for\w\b");??foreach (Match match in expression.Matches(words))??{??Console.Write(match + "\t");??}?
“\b”匹配單詞的邊界位置。運(yùn)行結(jié)果如圖? 所示。由結(jié)果可以看出,當(dāng)沒有定位 符時(shí),匹配單詞中所有符合要求的子串;當(dāng)定位符\b 在前面時(shí),在單詞的頭部尋找匹配的子串;當(dāng)定位符\b 在后面時(shí),在單詞的結(jié)尾尋找匹配的子串?
常用正則表達(dá)式“\b\w+\b”找出一句話里的所有單詞。?
用于正則表達(dá)式的定位符如表
?符號(hào)“^”和“$”在正則表達(dá)式中也有特殊用處,所以當(dāng)要 匹配它們本身時(shí)也需使用它們的轉(zhuǎn)義字符“\^”和“\$”。
?分組和后向引用
如果沒有括號(hào),正則表達(dá)式“tr|bee”匹配“tr”或“bee”,加了括號(hào)后,“(tr|b)ee”匹 配“tree”或“bee”,這種帶括號(hào)的形式稱為括號(hào)表達(dá)式。
括號(hào)表達(dá)式并不簡單的起著確 定范圍的作用,它同時(shí)會(huì)創(chuàng)建子表達(dá)式,每個(gè)子表達(dá)式形成一個(gè)分組,并把捕獲到的與子 表達(dá)式匹配的子串保存在分組中,以供將來使用
舉一個(gè)例子,電子郵件地址通常由用戶名、二級(jí)域名、一級(jí)域名三部分構(gòu)成。比 如 waterwood @163.com,其中 waterwood 為用戶名,163 為二級(jí)域名,com 為一級(jí)域名。 我們可以通過下面的正則表達(dá)式匹配這類電子郵件地址:? ? ? ? "(\w+)@(\w+)\.(\w+)"
這里有三個(gè)括號(hào),形成三個(gè)子表達(dá)式,就會(huì)出現(xiàn)三個(gè)分組。默認(rèn)情況下,每個(gè)分組會(huì) 自動(dòng)擁有一個(gè)組號(hào),規(guī)則是:從左向右,按分組左括號(hào)的出現(xiàn)順序進(jìn)行編號(hào),第一個(gè)分組 的組號(hào)為 1,第二個(gè)為 2,以此類推。在正則表達(dá)式里引用分組的語法為“\number”,比如 “\1”代表與分組 1 匹配的子串,“\2”代表與分組 2 匹配的字串,等等
如果出現(xiàn)嵌套的分組,它們的編號(hào)也按左括號(hào)的出現(xiàn)順序排列。
存儲(chǔ)起來的分組有什么作用呢?下面我們通過一個(gè)例子來說明。很多句子里會(huì)有 重復(fù)的單詞,下面我們就通過正則表達(dá)式找出這些重復(fù)的單詞。
string text = "you are very very good";??foreach (Match match in Regex.Matches(text, @"\b(\w+)\b \1\b"))??{??Console.WriteLine(match);?}? 運(yùn)行結(jié)果如圖 ?所示
我們前面已經(jīng)知道“\b(\w+)\b”匹配一個(gè)單詞,因?yàn)檫@里面有一個(gè)括號(hào),所以會(huì)捕獲 一個(gè)以 1 為組號(hào)的分組,后面的“\1”就代表這個(gè)分組,表示這個(gè)位置上出現(xiàn)和分組 1 一 樣的內(nèi)容。所以兩者連起來就表示兩個(gè)重復(fù)的單詞。這種在后面的表達(dá)式中引用前面的分組的方式就叫做后向引用
除了匹配重復(fù)的單詞,后向引用還有一個(gè)較為常見的應(yīng)用,那就是匹配有效的 HTML 標(biāo)簽。我們知道,HTML 標(biāo)簽大多是成對出現(xiàn)的?
string words = "valid
not valid";??foreach (Match match in Regex.Matches(words, @"<(.*?)>.*?\1>"))??{??Console.WriteLine(match);??}?
運(yùn)行結(jié)果如圖
string phonebook = "(010)88665987 (020)23658945 (021)88965222";??string pattern = @"\((\d{3,4})\)(\d{7,8})";??string result = Regex.Replace(phonebook, pattern, "$1-$2");?Console.WriteLine(result);??
替換文本
通過Regex類的Replace() 方法就能以特定字符串替換原字符串中與正則表達(dá)式匹配的子串。
在大部分語言的正則表達(dá)式中,查找時(shí),使用后向引用的語法為“\number”;而在替 換時(shí),其語法為“$number”。例子中的 Regex.Replace ()方法把與正則表達(dá)式匹配的子串替 換為“$1-$2”,其中$1 對應(yīng)與分組 1(即區(qū)號(hào)),$2 對應(yīng)分組 2(即電話號(hào)碼)。運(yùn)行結(jié)果 如圖 所示
正則表達(dá)式?\((\d{3,4})\)(\d{7,8})?用于匹配一個(gè)特定的電話號(hào)碼格式,其中電話號(hào)碼由兩部分組成:一個(gè)括號(hào)內(nèi)的區(qū)號(hào)(可能是3位或4位數(shù)字),以及一個(gè)緊跟在括號(hào)后的7位或8位數(shù)字。
這里是該正則表達(dá)式的分解:
\(: 匹配左括號(hào)?(?字符。因?yàn)樵谡齽t表達(dá)式中括號(hào)是特殊字符,所以需要使用反斜杠?\?進(jìn)行轉(zhuǎn)義。(\d{3,4}): 這是一個(gè)捕獲組(因?yàn)樗焕ㄌ?hào)包圍),它匹配3位或4位的數(shù)字。\d?是數(shù)字的簡寫,{3,4}?表示前面的?\d?可以出現(xiàn)3次或4次。\): 匹配右括號(hào)?)?字符,同樣也需要轉(zhuǎn)義。(\d{7,8}): 這是另一個(gè)捕獲組,它匹配7位或8位的數(shù)字。
在.NET 中使用正則表達(dá)式進(jìn)行替換時(shí),分組的命名方式為:(?
正向預(yù)查?=? /? ?反向預(yù)查?<=? ?/??負(fù)正向預(yù)查?!
有這樣一段文本,它包含了六個(gè)員工的電話號(hào)碼,如何能匹配到所有微軟員工的 電話號(hào)碼而不匹配其它公司的電話號(hào)碼呢?Jack boy 29 010-88127631 Microsoft, Sally 22 girl 010-88127632 Microsoft,?Ben boy 26 020-65423541 Google,Merry 23 girl 020-65423542 Google,?Alan boy 27 021-23456851 Apple,Kitty girl 18 021-23456852 Apple.? 在正則表達(dá)式中,正向預(yù)查能幫我們解決這個(gè)問題,其語法為在分組中添加元字符 “?=”。string text = @"Jack boy 29 010-88127631 Microsoft, Sally girl 22 010-88127632?Microsoft,Ben boy 26 020-65423541 Google,Merry girl 23?020-65423542 Google,Alan boy 27 021-23456851 Apple,Kitty girl?18 021-23456852 Apple.";??Console.WriteLine("The Telephone numbers of Microsoft are:");
如果僅僅使用正則表達(dá)式“\d{3}-\d{8}”,它就會(huì)匹配文本里所有的電話號(hào)碼,那么如 何讓它只匹配微軟的電話號(hào)碼呢?這時(shí)就需要在它后面加上限制條件“(?= Microsoft)”。
需要強(qiáng)調(diào)的是正向預(yù)查表達(dá)式只是用來篩選查詢目標(biāo),并不屬于查詢目標(biāo)本身,不包 含在最終結(jié)果中。運(yùn)行結(jié)果如圖
反向預(yù)查?<=?
正向預(yù)查用來篩選查詢目標(biāo),限制條件在查詢目標(biāo)的后面,反向預(yù)查也用來篩選查詢 目標(biāo),但限制條件在查詢目標(biāo)的前面,其語法是在分組中添加元字符“?<=”。
正則表達(dá)式“(?<=girl \d\d )\d{3}-\d{8}”匹配這樣的電話號(hào)碼,它們前面的文本必須 與“girl \d\d ”匹配。
負(fù)正向預(yù)查?!
負(fù)正向預(yù)查與正向預(yù)查相反,查詢目標(biāo)后面不能跟指定的字符串,其語法為在分組中 添加元字符“?!”
正則表達(dá)式“\d{3}-\d{8}(?! Microsoft)”匹配那些后面不跟“ Microsoft”的電話號(hào)碼
負(fù)反向預(yù)查?
負(fù)反向預(yù)查與反向預(yù)查相反,查詢目標(biāo)前面不能跟指定字符串,其語法是在分組中添 加元字符“?
正則表達(dá)式“(?
正則表達(dá)式的選項(xiàng)
通過 Regex 類的 Options 屬性可以設(shè)置正則表達(dá)式的選項(xiàng),表 20-5 是.Net 中常用 的正則表達(dá)式選項(xiàng)
正則表達(dá)式默認(rèn)是區(qū)分大小寫的,可以通過 IgnoreCase 選項(xiàng)設(shè)置為不區(qū)分大小寫。
string words = "ROOM215 Room315 room415";?string pattern = @"Room\d{3}";??Regex expression = new Regex(pattern, RegexOptions.IgnoreCase);??foreach (Match match in expression.Matches(words))??{??Console.WriteLine(match);??}
運(yùn)行結(jié)果如圖
如果想同時(shí)設(shè)置多個(gè)選項(xiàng),可以把它們用按位或“|”連接起來。 new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
注釋#?
遇到復(fù)雜的、比較難理解的正則表達(dá)式時(shí),我們可以通過添加注釋的方式進(jìn)行解釋說 明。在正則表達(dá)式中添加注釋的語法為:(?#注釋內(nèi)容)。比如 ? ? ? ? ? ? @"\((\d{3,4})(?#該分組匹配區(qū)號(hào))\)(\d{7,8})(?#該分組匹配電話號(hào)碼)"? ? ? ? ? ? ? 編譯器把以“?#”開頭的分組當(dāng)作注釋,這些注釋在編譯時(shí)都會(huì)被忽略掉
練習(xí):驗(yàn)證用戶輸入?
private void btnOK_Click(object sender, EventArgs e)?{??string pattern = @"^SN\w{2}-\d{4}-[abc]{4}-[@#]{4}$";??if (Regex.IsMatch(TextBox.Text, pattern))??{??MessageBox.Show("注冊成功!");??}??else??{??MessageBox.Show("注冊碼錯(cuò)誤,請重新輸入");??}?}?
當(dāng)單擊“確定”按鈕時(shí),Regex.IsMatch()方法會(huì)檢驗(yàn)用戶輸入的字符串是否滿足規(guī)則, 如果滿足規(guī)則,則提示注冊成功;如果不滿足規(guī)則,則提示用戶重新輸入。
柚子快報(bào)邀請碼778899分享:正則表達(dá)式
精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。