正則表達(dá)式是判斷栖袋、匹配和分解一定模式的字符串的法寶。C#
強(qiáng)大的基礎(chǔ)類庫(kù)提供的System.Text.RegularExpressions
命名空間里包含的Regex
類,可以充分發(fā)揮正則表達(dá)式的威力吟榴。
需要查看C#
中正則表達(dá)式里各個(gè)符號(hào)的意義的請(qǐng)查閱這里,不過(guò)看一遍也很難記住囊扳,可以用的時(shí)候再查詢吩翻,用的次數(shù)多了就會(huì)記住那些使用頻率較高的符號(hào)了。
0. 簡(jiǎn)單介紹C#里Regex類的用法
(1)檢查一個(gè)字符串是否符合指定的模式宪拥,如pattern="^[0-9]*$"
Regex regex = new Regex(pattern);
if (regex.IsMatch(test))
Console.WriteLine("{0} matches the pattern.", test);
else Console.WriteLine("{0} does not match the pattern.", test);
(2)提取一定模式的字符串
Match match = regex.Match(text);
MatchCollection matches = regex.Matches(text);
如果能夠確定text里面只有一個(gè)子串滿足模式pattern仿野,可以用Match
;如果可能有多個(gè)匹配她君,則可以使用Matches
脚作。當(dāng)然若我們只需要從包含多個(gè)pattern的text中找到第一個(gè),那么也可以用Match
。
Match
中兩個(gè)重要的屬性是Value
和Index
球涛,其中Value
為滿足設(shè)定模式的子串劣针,Index
為該子串在輸入字符串中的開(kāi)始位置。
另外亿扁,如果我們匹配的模式中有多個(gè)項(xiàng)捺典,如"\b(?<word>\w+)\s+(\k<word>)\b"
表示重復(fù)的單詞,它的模式串中有兩個(gè)word从祝,我們最后得到的Match.Value
可能是“dog dog”襟己,而若我們想得到單個(gè)單詞的話還需要進(jìn)一步對(duì)這個(gè)子串進(jìn)行劃分。但是Match
中還有一個(gè)重要的屬性已經(jīng)幫我們做好了這一步牍陌,Match.Groups
獲取由正則表達(dá)式匹配的組的集合擎浴,對(duì)于上面的例子Match.Groups["word"].Value
就表示“dog”。
Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
string text = "The the quick brown fox fox jumped over the lazy dog dog.";
Match singleMatch = rx.Match(text);
Console.WriteLine("Value: {0}", singleMatch.Value);
Console.WriteLine("Index: {0}", singleMatch.Index);
MatchCollection matches = rx.Matches(text);
foreach (Match match in matches)
{
GroupCollection groups = match.Groups;
Console.WriteLine("'{0}' repeated at positions {1} and {2}",
groups["word"].Value,
groups[0].Index,
groups[1].Index);
}
(3)替換匹配的子串
string replactedText = rx.Replace(text, myEvaluator);
這里的參數(shù)myEvaluator
是MatchEvaluator
委托。下面的例子中我們專門(mén)定義了方法ReplaceCC2C()
來(lái)將重復(fù)出現(xiàn)的單詞去掉一個(gè)(即替換成單個(gè)單詞)。MatchEvaluator
委托的輸入是Match
做瞪,輸出是要替換Match.Value
的字符串。
如果替換的方法比較簡(jiǎn)單時(shí)仿吞,我們可以省去專門(mén)定義一個(gè)方法的力氣,直接使用匿名方法捡偏,如
var test = rx.Replace(text, delegate(Match m) { return m.Groups["word"].Value; });
public void ExtractMatches()
{
Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
string text = "The the quick brown fox fox jumped over the lazy dog dog.";
RegexExample ex = new RegexExample();
MatchEvaluator myEvaluator = new MatchEvaluator(ex.ReplaceCC2C);
var replactedText = rx.Replace(text, myEvaluator);
//var replactedText = rx.Replace(text, delegate(Match m) { return m.Groups["word"].Value; });
Console.WriteLine(text);
Console.WriteLine(replactedText);
}
public string ReplaceCC2C(Match m)
{
return m.Groups["word"].Value;
}
輸出結(jié)果:
The the quick brown fox fox jumped over the lazy dog dog.
The quick brown fox jumped over the lazy dog.
(4)拆分字符串
string[] parts = rx.Split(text);
雖然C#
中string
也有Split
函數(shù)唤冈,但是它的參數(shù)只能是確定的字符數(shù)組,而Regex
的Split
函數(shù)則是利用正則表達(dá)式模式將輸入字符串拆分成子字符串?dāng)?shù)組霹琼。
下面我們從一些常見(jiàn)的例子來(lái)嘗試使用正則表達(dá)式务傲,上面已經(jīng)介紹的Regex
的用法,下面的例子都是檢查是否滿足某模式串枣申,都是用IsMatch
售葡,所以接下來(lái)我只會(huì)列出模式串。
1. 驗(yàn)證我國(guó)的座機(jī)電話號(hào)碼
首先我們列出所有想要支持的電話號(hào)碼的格式忠藤,然后再分類看怎么寫(xiě)正則表達(dá)式挟伙。
規(guī)則:我國(guó)的座機(jī)號(hào)碼分為區(qū)號(hào)和號(hào)碼,區(qū)號(hào)與號(hào)碼之間用短橫‘-’或者括號(hào)‘()’來(lái)分割模孩,另外最前面還可能會(huì)加上我國(guó)的區(qū)號(hào)86或者+86尖阔,下面列舉出一些合法的電話號(hào)碼:
027-88888888
0722-7777777
(027)88888888
(0722)7777777
86(027)88888888
+86(0722)7777777
所以最后寫(xiě)出的表達(dá)式為:
"^(\+)?(86)?0\d{2,3}-\d{7,8}$|^(\+)?(86)?\(0\d{2,3}\)\d{7,8}$"
另外,我看到有人這樣寫(xiě)"0\d{2,3}-\d{7,8}|\(0\d{2,3}\)\d{7,8}"
榨咐,雖然上面的例子都能通過(guò)介却,但是它可能會(huì)使得027-888888889999也是合法的,因?yàn)樯鲜霰磉_(dá)式是在輸入字符串里找到一個(gè)子串滿足我們的模式要求块茁,只要存在這樣的子串IsMatch
就會(huì)返回true
齿坷。所以我們需要在對(duì)模式串加上開(kāi)始字符^
和結(jié)束字符$
來(lái)避免這種情況桂肌。
2. 驗(yàn)證手機(jī)電話號(hào)碼
規(guī)則:
手機(jī)號(hào)碼為11位數(shù)字,首位一定是1永淌,到目前為止第二位數(shù)字可以是3,4,5,7,8崎场,其他位可以是0-9的任意數(shù)字。格式上可以是所有數(shù)字連寫(xiě)遂蛀,也可以在中間加上短橫分割谭跨。
正例:
13012345678
15887654321
178-23760930
178-2376-0930
反例:
1301234567
158876543212
178-2376-0930-
正則表達(dá)式:"^1[34578]+\d{1}((-)?\d{4}){2}$"
逐字符解釋:^
表示電話號(hào)碼的開(kāi)始,1
表示第一位字符一定要是1李滴,[34578]+
表示第二位字符是3,4,5,7,8中的至少一個(gè)螃宙,\d{1}
表示一個(gè)數(shù)字,同理\d{4}
表示4個(gè)數(shù)字悬嗓,(-)?
表示短橫可以出現(xiàn)0次或1次污呼,((-)?\d{4}){2}
表示(-)?\d{4}
出現(xiàn)兩次,$
表示電話號(hào)碼結(jié)束包竹。
3. 驗(yàn)證電子郵箱
規(guī)則:必須以字母開(kāi)始(有人說(shuō)有郵箱可以以下劃線開(kāi)始,比如說(shuō)Yahoo籍凝,但是我專門(mén)去注冊(cè)試了試周瞎,人家要求一定要以字母開(kāi)始);電子郵箱中包含且僅包含一個(gè)@字符饵蒂,該字符將郵箱劃分為兩個(gè)部分声诸;前部分為用戶名,可以由字母退盯、數(shù)字彼乌、下劃線、短橫或點(diǎn)組成渊迁;不能出現(xiàn)多個(gè)短橫或點(diǎn)相連的情況慰照;
正例:
12345@qq.com
test.Name@163.com
full_name@google.com
__12-345@qq.com
反例:
__12--345@qq.com
正則表達(dá)式:"^[A-Za-z]+([-.]\w+)*@\w+([-.]\w+)*\.[a-z]{2,3}$"
逐字符解釋:^[A-Za-z]+
表示至少有一個(gè)字母,\w+
表示至少一個(gè)包括下劃線的任何單詞字符琉朽,等價(jià)于[A-Za-z0-9_]+
毒租,([-.]\w+)*
表示零個(gè)或者多個(gè)[-.]\w+
,而[-.]\w+
表示-
或者.
加上包括下劃線的任何單詞字符箱叁,這樣寫(xiě)正好可以把多個(gè)短橫或者點(diǎn)號(hào)用字符分隔開(kāi)墅垮,@
字符后面的前半部分與之前的類似,最后一部分為\.[a-z]{2,3}
耕漱,表示后綴為.
加上2個(gè)或者2個(gè)小寫(xiě)字母算色。
注:有人可能會(huì)說(shuō)qq郵箱前面應(yīng)該全部都是數(shù)字,所以上面的表達(dá)式不松了螟够。這就看大家的使用環(huán)境了灾梦,比如說(shuō)有些郵箱對(duì)用戶名的長(zhǎng)度也有限制,所以還是不要想一勞永逸了吧。只要在使用的時(shí)候把應(yīng)用場(chǎng)景下的所有情況列舉出來(lái)斥废,能夠滿足要求即可椒楣。
4. 驗(yàn)證身份證號(hào)碼
規(guī)則:早期的身份證號(hào)碼是15位的全數(shù)字;現(xiàn)在的身份證號(hào)碼為18位牡肉,由17位數(shù)字和1位校驗(yàn)字符組成捧灰;最后的校驗(yàn)字符可能0-10,其中10由字母X或者x表示统锤。
正例:
429001197806281
429001197806281234
42900119780628123X
42900119780628123x
反例:
42900119780628123a
4290011978062812
4290011978062812345
正則表達(dá)式:"^\d{15}$|^\d{18}$|^\d{17}[Xx]+$"
逐字符解釋:以上分了三種情況毛俏,\d{15}
表示15位數(shù)字,\d{17}[Xx]+
表示17位數(shù)字再加上一位X或者x饲窿。
注:其實(shí)現(xiàn)在身份證上的前面17位數(shù)字也不是任意的0-9的數(shù)字煌寇,比如說(shuō)出生日期里的年月日都應(yīng)該是有限制的,上述表達(dá)式并沒(méi)有嚴(yán)格考慮這些逾雄,大家根據(jù)需要增加更嚴(yán)格的驗(yàn)證阀溶。
5. 驗(yàn)證IP地址
規(guī)則:點(diǎn)分十進(jìn)制的IPV4地址,格式為X.X.X.X鸦泳,其中X為0-255银锻。
正例:
127.0.0.1
255.255.255.255
0.0.0.0
248.250.198.23
反例:
256.0.0.0
00.0.0.1
127.012.032.1234
正則表達(dá)式:"^((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)$"
逐字符解釋:為了更好地表示IP地址中對(duì)數(shù)字的限制,這里我們將0-255劃分成了多個(gè)區(qū)段250-255,240-249,100-199,10-99,0-9
小結(jié)一些常見(jiàn)的模式
^
做鹰,匹配字符串的開(kāi)始
\d
击纬,用來(lái)匹配0到9的數(shù)字
\w
,用來(lái)匹配字母數(shù)字或者下劃線
(xx)?
钾麸,用來(lái)表示xx出現(xiàn)零次或者一次
(xx)+
更振,用來(lái)表示xx至少出現(xiàn)一次
(xx)*
,用來(lái)表示xx出現(xiàn)零次或者多次
(xx){2,3}
饭尝,用來(lái)表示xx出現(xiàn)兩次或者三次
[123abc]
肯腕,其中[]用來(lái)表示里面出現(xiàn)的字符是或的關(guān)系
$
,匹配字符串的結(jié)束