標籤:style blog http java color 使用
本次課程中講的有的東西都是根據初學者的認知規律進行了調整,並不是嚴謹的,比如很多地方在多AppDomain條件下很多說法就不對了,但是說嚴謹了大家就暈了,因此繼續不嚴謹的講吧。 很多面試題都在這階段的課程中。 .Net進階技術屬於拔高內容,根據自己的基礎決定學習深度。 參考教材: 《C#進階編程》 《C#圖解教程》 《CLR Via C#》
Regex前奏:地獄
需求1:“192.168.10.5[port=8080]”,這個字串表示IP地址為192.168.10.5的伺服器的8080連接埠是開啟的,請用程式解析此字串,然後列印出“IP地址為***的伺服器的***連接埠是開啟的”。
需求2:“192.168.10.5[port=21,type=ftp]”,這個字串表示IP地址為192.168.10.5的伺服器的21連接埠提供的是ftp服務,其中如果“,type=ftp”部分被省略,則預設為http服務。請用程式解析此字串,然後列印出“IP地址為***的伺服器的***連接埠提供的服務為***”
需求3:判斷一個字串是否是Email?必須含有@和.、不能以@或者.開始或者結束、@要在最後一個.之前
需求4:從一個文本中提取出所有的Email:我有全部333M的照片,要的給我發email:[email protected]。我也要[email protected],[email protected],樓主好人:[email protected]。 需求5:提取網頁中的所有圖片、超連結。
Regex入門:天堂
Regex是用來進行文本處理的技術,是語言無關的,在幾乎所有語言中都有實現。javascript中還會用到。
一個Regex就是由一般字元以及特殊字元(稱為元字元)組成的文字模式。該模式描述在尋找文字主體時待匹配的一個或多個字串。Regex作為一個模板,將某個字元模式與所搜尋的字串進行匹配。 就像萬用字元“*.jpg”、“%ab%”,它是對字串進行匹配的特殊字元串 Regex是非常複雜的,不要希望一次都掌握,理解Regex能做什麼(字串的匹配、字串的提取、字串的替換),掌握常用的Regex用法,以後用到再查就行。 找工作的亮點。後面項目中的敏感詞過濾、Validator等也會涉及到Regex。
元字元1
要想學會Regex,理解元字元是一個必須攻克的難關。不用刻意記
.:匹配任何單個字元。例如Regex“b.g”能匹配如下字串:“big”、“bug”、“b g”,但是不匹配“buug”,“b..g”可以匹配“buug”。
[ ] :匹配括弧中的任何一個字元。例如Regex“b[aui]g”匹配bug、big和bag,但是不匹配beg、baug。可以在括弧中使用連字號“-”來指定字元的區間來簡化表示,例如Regex[0-9]可以匹配任何數字字元,這樣Regex“a[0-9]c”等價於“a[0123456789]c”就可以匹配“a0c”、“a1c”、“a2c”等字串;還可以制定多個區間,例如“[A-Za-z]”可以匹配任何大小寫字母,“[A-Za-z0-9]”可以匹配任何的大小寫字母或者數字。
( ) :將 () 之間括起來的運算式定義為“組”(group),並且將匹配這個運算式的字元儲存到一個臨時地區,這個元字元在字串提取的時候非常有用。把一些字元表示為一個整體。改變優先順序、定義提取組兩個作用。
| :將兩個匹配條件進行邏輯“或”運算。‘z|food‘ 能匹配 "z" 或 "food"。‘(z|f)ood‘ 則匹配 "zood" 或 "food"。
元字元2
*:匹配0至多個在它之前的子運算式,和萬用字元*沒關係。例如Regex“zo*”能匹配 “z” 、“zo”以及 “zoo”;因此“.*”意味著能夠匹配任一字元串。"z(b|c)*"→zb、zbc、zcb、zccc、zbbbccc。"z(ab)*"能匹配z、zab、zabab(用括弧改變優先順序)。
+ :匹配前面的子運算式一次或多次,和*對比(0到多次)。例如Regex9+匹配9、99、999等。 “zo+”能匹配 “zo”以及 “zoo” ,不能匹配"z"。
? :匹配前面的子運算式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 。一般用來匹配“可選部分”。
{n} :匹配確定的 n 次。"zo{2}"→zoo。例如,“e{2}” 不能匹配“bed”中的“e”,但是能匹配“seed”中的兩個“e”。
{n,} :至少匹配n次。例如,“e{2,}”不能匹配“bed”中的“e”,但能匹配 “seeeeeeeed”中的所有“e”。
{n,m} :最少匹配 n 次且最多匹配 m 次。“e{1,3}”將匹配“seeeeeeeed”中的前三個“e”。
元字元3
^(shift+6) :匹配一行的開始。例如Regex“^regex”能夠匹配字串“regex我會用”的開始,但是不能匹配“我會用regex”。
^另外一種意思:非!(暫時不用理解)
$ :匹配行結束符。例如Regex“浮雲$” 能夠匹配字串“一切都是浮雲”的末尾,但是不能匹配字串“浮雲呀”
簡寫運算式
注意這些簡寫運算式是不考慮轉義符的,這裡的\就表示字元\,而不是C#字串層級的\,在C#代碼中需要使用@或者\雙重轉義。區分C#層級的轉移和Regex層級的轉移,恰好C#的轉義符和Regex的轉義符都是\而已。Regex的轉移是在C#之後的(層層盤剝)。把C#的轉義符想成%就明白了。在C#看來@"\-"就是\-這個普通的字串,只不過在Regex分析引擎看來他有了特殊含義。"\\d"或者@"\d"
\d:代表一個數字,等同於[0-9]
\D:代表非數字,等同於[^0-9]
\s:代表分行符號、Tab定位字元等空白字元
\S:代表非空白字元
\w:匹配字母或數字或底線或漢字,即能組成單詞的字元
\W:非\w ,等同於[^\w]
d:digital;s:space、w:word。大寫就是“非”
.Net中的Regex1
參考:《C#進階編程》7.3。發元字元解釋PPT
Regex在.Net就是用字串表示,這個字串格式比較特殊,無論多麼特殊,在C#語言看來都是普通的字串,具體什麼含義由Regex類內部進行文法分析。 Regex(Regular Expression)的主要類:Regex
常用的3種情況:(C#文法)
判斷是否匹配:Regex.IsMatch(“字串”,”Regex”);
字串提取:Regex.Match(“字串”,“要提取的字串的Regex”);
字串提取(迴圈提取所有): Regex.Matches()
字串替換:Regex.Replace(“字串”,”正則”,”替換內容”);
.Net中的Regex2
Regex.IsMatch方法用於判斷一個字串是否匹配Regex。
字串匹配例子:
Regex.IsMatch("bbbbg","^b.*g$");
Regex.IsMatch("bg", "^b.*g$");
Regex.IsMatch("gege", "^b.*g$");
一定不能忘了^和$,否則也能匹配yesbagit
字串匹配案例1
練習1:判斷是否是合法的郵遞區號(6位元字)
Regex.IsMatch("100830","^[0-9]{6}$")
Regex.IsMatch("119", @"^\d{6}$");
解釋:由元字元定義得知“[0-9]”表示0到9的任一字元,“{6}”表示前面的字元匹配6此,因此“[0-9]{6}”中的{6}表示對數字匹配6次。簡寫運算式得知“[0-9]”可以被“\d”代替,所以第二種寫法“\d{6}”也是正確的。
字串匹配案例2
判斷一個字串是不是社會安全號碼碼,即是否是15或18位元字。
錯誤寫法:Regex.IsMatch(“123456789123456789”, @“^\d{15}|\d{18}$”),表示15位元字開頭或者18位元字結尾.//匹配“以15位元字開始”或者“以18位元字結束”( | 的優先順序最低,最後執行)
正確寫法:Console.WriteLine(Regex.IsMatch("0111111111111111", @"^\d{15}$|^\d{18}$"))或者@"^(\d{15}|\d{18})$"
字串匹配案例3
判斷字串是否為正確的國內電話號碼,不考慮分機。
010-8888888或010-88888880或010xxxxxxx
0335-8888888或0335-88888888(區號-電話號)
10086、10010、95595、95599、95588(5位)
13888888888(11位都是數字)
Regex.IsMatch(phoneNumber, @“^((\d{3,4}\-?\d{7,8})|(\d{5})|(\d{11}))$");
按照要求一個一個寫,都用|連起來。注意:由於區號有時為010-xxxxxxx有時為010xxxxxxx,-可有可無,所以需要?,由於-表示一個區間,所以這裡要轉義\-。最後不要忘記在所有|的最外層加一對()
while (true) { string phoneNumber = Console.ReadLine(); bool b = Regex.IsMatch(phoneNumber, @"^((\d{3,4}\-\d{7,8})|(\d{5})|(\d{11}))$"); Console.WriteLine(b); }
字串匹配案例4
判斷一個字串是否是合法的Email地址。一個Email地址的特徵就是以一個字元序列開始,後邊跟著“@”符號,後邊又是一個字元序列,後邊跟著符號“.”,最後是字元序列 Regex.IsMatch("[email protected]", @"^\[email protected]\w+\.\w+$");
[]括弧中的任一字元,\w字母、數字、底線,+一到多個。由於.在Regex中有特殊的含義,因此對於真正想表達“.”則需要轉移“\.”。
Email:^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+([;.](([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+)*$
字串匹配練習
1、匹配IP地址,4段用.分割的最多三位元字。 192.168.54.77、333.333.333.333假設都是正確的。
2、判斷是否是合法的日期格式“2008-08-08”。四位元字-兩位元字-兩位元字。
3、判斷是否是合法的url地址,http://www.test.com/a.htm、ftp://127.0.0.1/1.txt。字串序列://字串序列。@"^\w+://.+$"。//簡化的識別,項目中你搜“w3c URL Regex”。.+而不是\w,否則"?id=1"中的?就不能匹配了。 http://www.test.com/a.aspx?id=1
元字元中的字元,如果想直接匹配的話都需要轉移: \. \+ \? \+ \- \* ……….
=================================================
取巧的辦法:從ASP.Net的RegularExpressionValidator中抄常用的Regex,工作中一般是從網上找現成的。或者去http://www.regexlib.com/ 搜尋。
IP地址Regex:^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$ URL匹配:^(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(\:[0-9]+)*(/($|[a-zA-Z0-9\.\,\?\‘\\\+&%\$#\=~_\-]+))*$ (0[1-9])|(1[012])匹配月份
字串提取-提取所有“超連結”
MatchCollection matches = Regex.Matches(str, "<a href=\".*?\">.*?</a>"); foreach (Match item in matches) { if (item.Success) { Console.WriteLine(item.Value); } }@“<a((\s*?)(.+?)(\s*?))>.+?</a>“ //空白符無法匹配。使用\s.不匹配斷行符號、換行等空白字元。Regex.Match()只能匹配第一個,怎麼辦?match的Success屬性工作表示是否匹配成功;提取文檔中所有匹配該Regex的字串。介紹貪婪模式。test1.htm@“<a((\s*?)(.+?)(\s*?))>.+?</a>“ //空白符無法匹配。使用\s//提取字串的時候不需要用^$
string str = "<a href=\"www.baidu.com\">baidu</a>fdsfdsfdsfdfd<a href=\"www.google.com\">google</a>亂七八糟什麼都有ffdsf<a href=\"www.163.com\">163</a>s輔導書frewrewre<a href=\"www.sohu.com\">sohu</a><h1>這個是測試!!</h1><a href=\"www.sina.com.cn\">sina</a>sfdsfdss";@“<a((\s*?)(.+?)(\s*?))>.+?</a>“ //空白符無法匹配。使用\s
貪婪模式與非貪婪模式
從文本提取出名字:
Match match = Regex.Match("大家好。我是S.H.E。我22歲了。我病了,嗚嗚。", "我是(.+)。");//沒有加^$。
看結果。+、*、{n}、{n,}、{n,m}的匹配預設是貪婪(greedy)的:儘可能多的匹配,直到“再貪婪一點兒”其後的匹配模式就沒法匹配為止。
在+、*後添加?就變成非貪婪模式(? 的另外一個用途):讓其後的匹配模式儘早的匹配。修改成"我是(.+?)。"
一般開發的時候不用刻意去修飾為非貪婪模式,只有遇到bug的時候發現是貪婪模式的問題再去解決。
提取網頁中的所有url提取網頁中的所有http://.....沒有在<a ></a>標籤中的也要提取 MatchCollection matches = Regex.Matches(htmltxt, @"http://[a-zA-z0-9\-_\?&=\.]+"); foreach (Match match in matches) { Console.WriteLine(match.Value); }