地獄-天堂之說,源自老程序員的話.老程序員告訴我們,沒有正則表達式就像地獄一般,有了正則表達式我們就像進了天堂一樣.好,我們下面看這么幾個需求:
需求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:me@wo.com。我也要you@you.com,123456@163.com,樓主好人:888888@qq.cn。
(論壇中如百度貼吧中,可以自己做個種子搜索器.請看下圖)
一、查找網頁中的e-mail
類似於這種情況,我們用普通的方法實現非常的困難。有個非常好的處理方法,就是正則表達式。正則表達式就是對字符串的操作。
正則是語言無關的,很多的語言當中都能用到正則表達式。
那么,到底正則表達式是個啥呢?實際上就是一些通配符。
正則給我們一些元字符,這些元字符可以理解成一些已經定義好的通配符,使用它們在字符串中找到我們想要的匹配。
那么正在等用處很多,像采集器、敏感詞過濾、URLRewite、Validator中都會用到。
下面我們介紹一些元字符:
第一種是: .:匹配除\n之外的任何單個字符。
第二種是: [ ] :匹配括號中的任何一個字符。 如果要想匹配二十六個英文字母中的任意一個怎么寫呢? a[a-z]b 如果也想匹配大寫的A—Z的話,需要這么寫 a[a-zA-Z]b
第三種是: | :將兩個匹配條件進行邏輯“或”運算。 如a[a|b]b z|food 要么是z,要么是food (z|f)ood 加小括號改變優先級
第四種是: ( ) :將 () 之間括起來的表達式定義為“組”(group),並且將匹配這個表達式的字符保存到一個臨時區域,這個元字符在字符串提取的時候非常有用。把一些字符表示為一個整體。改變優先級、定義提取組兩個作用。
元字符2(限定符):
第一種是: *:匹配0至多個在它之前的子表達式,和通配符*沒關系。 如 zoo* ,*表示可以出現0次,也可以出現多次。(可有可無,可多可少)
第二種是: + :匹配前面的子表達式一次或多次 如zoo+ ,+表示可以出現一次,或者是多次。(至少得出現一次。) 注意:* +只表示o的出現次數,要是想表示前面的整個表達式,得用小括號括起來。
第三種是: ? :匹配前面的子表達式零次或一次。 如zoo? ,要么0次,要么1次。
第四種是: {n} :匹配確定的 n 次。
第五種是: {n,m} :最少匹配 n 次且最多匹配 m 次。
元字符3
第一種是: ^(shift+6) :匹配一行的開始。如^xxx. 表示字符串必須以三個先開頭,后面跟任意的單個字符。
它還有一個意思就是取反的意思,如 a[^a-zA-Z0-9]b 表示中間去大小寫字符和數字都不行。
第二種是: $ :匹配行結束符。 如xxx.$ 表示結尾必須以xxx加任意字符結束。
接下來我們再看一些個元字符(一些簡寫的方式):
第一種是: \d:代表一個數字,等同於[0-9] 如a[0-9]b a\db
第二種是: \D:代表非數字,等同於[^0-9]
第三種是: \s:代表換行符、Tab制表符等空白字符 ,(空格、回車、制表符)
第四種是: \S:代表非空白字符(a0%$@@)
第五種是: \w:匹配字母或數字或下划線或漢字,即能組成單詞的字符,除%&#@!$等字符。[a-zA-Z0-9_漢字]
第六種是: \W:非\w ,等同於[^\w] %
需要注意的地方:當放在C#中時,需要在它們之前加個\
下面我們主要講解怎么去用這些正則表達式:
二、使用正則需要用到的類及導入命名空間
三、正則的幾種用法
四、這是個什么類?
五、驗證郵箱是否匹配代碼
六、不能這么寫
字符串不能這么寫,字符串不能按數學上的區間來表示,寫正則永遠都是先找規律。
七、找規律
八、驗證用戶輸入的數字字符串代碼
字符串匹配例子:
Regex.IsMatch("bbbbg","^b.*g$");匹配,以b開頭以g結尾,中間出現任意字符
Regex.IsMatch("bg", "^b.*g$");匹配,中間有也行,沒有也行。
Regex.IsMatch("gege", "^b.*g$");不匹配,開頭要求是b,但是字符串不是。
下面寫個驗證身份證號碼的正則,要求:要么是15位,要么是18位的數字。
擴展在15位的時候,前14位是數字,最后一位是大小寫x。
九、驗證身份證號碼代碼
十、小作業
這個題我們可以在VS里面調試一下,試試。
十一、小作業答案
十二、衍生題目
十三、電話號碼驗證代碼
驗證如果非常的靈活的話,可以把驗證單獨提取到配置文件里面。將來正則表達式直接讀取配置文件就可以了。配置文件想怎么改怎么改。
十四、驗證email的正則
十五、自己寫的郵箱驗證
關於正則比較討厭的問題是,它不好測試。出錯之后必須自己去分析。
下面我們做下如下幾個練習:
1、匹配IP地址,4段用.分割的最多三位數字。 192.168.54.77、333.333.333.333假設都是正確的。
1.2.222.3
192.168.0.156
十六、驗證ip的正則代碼
2、判斷是否是合法的日期格式“2008-08-08”。四位數字-兩位數字-兩位數字。
十七、判斷是否是合法的日期格式代碼
3、判斷是否是合法的url地址,http://www.test.com/a.htm?id=3&name=aaa、ftp://127.0.0.1/1.txt。字符串序列://字符串序列。
十八、判斷url地址的正則
答案完整版:
1、匹配IP地址,4段用.分割的最多三位數字。 192.168.54.77、333.333.333.333假設都是正確的。 "@“^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$”。.是正則表達式中的特殊含義,因此需要轉義。進一步嚴 謹:^(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])$ "2、判斷是否是合法的日期格式“2008-08-08”。四位數字-兩位數字-兩位數字。@“^\d{4}\-\d{2}\-\d{2}$”,進一步嚴 謹:^\d{4}\-((0[1-9])|(1[0-2]))-((0[1-9])|(1[0-9])|(2[0-9])|(3[0-1]))$
3、判斷是否是合法的url地址,http://www.test.com/a.htm、ftp://127.0.0.1/1.txt。字符串序 列://字符串序列。@"^\w+://.+$"。//簡化的識別,項目中google搜“w3c URL 正則表達式”。.+而不是\w,否則"?id=1"中的?就不能匹配了。 http://www.test.com/a.aspx?id=1
取巧的辦法:從ASP.Net的RegularExpressionValidator中抄常用的正則表達式,工作中一般是從網上找現成的。或者去http://www.regexlib.com/ 搜索。
上面推薦了一個正則的網址,非常的好用,大概演示一下子。
十九、想搜什么對應的輸入就行了
下面我們看下正則表達式提取
二十、match方法
二十一、能不能for能不能foreach
二十二、為什么var不智能推斷了呢?
二十三、原因
二十四、提取所有用matches
下面我們提取網頁中所有的email
分享我多年收集的關於正則表達式的素材
二十五、把素材考進來
二十六、檢查下是否是亂碼
二十七、提取網頁中的email的代碼
請統計出126郵箱的使用人數,gmail郵箱的使用人數,並且把每個人的用戶名單獨的輸出。
可以用我們很早以前講過的split截取來做這個題.我們用正則表達式也能解決這個問題.
在介紹這個之前,我們先給大家介紹個稍微簡單點的.
二十八、提取組
二十九、看左括號
三十、用正則提取組解決剛才的問題
字符串提取練習1(提取組練習)
從文件路徑中提取出文件名(包含后綴) @"^.+\(.+)$"。比如從c:\windows\testb.txt中提取出testb.txt這個文件名出來。項目中用Path.GetFileName更好。貪婪模式。(想一下,這個時候怎么提取文件名?)
三十一、提取文件名練習
從“June 26 , 1951 ”中提取出月份June、26、1951來。@"([a-zA-Z]+)\s+\d{1,2},\s*\d{4}"進行匹配。月份和日之間是必須要有空格 分割的,所以使用空白符號“\s”匹配所有的空白字符,此處的空格是必須有的,所以使用“+”標識為匹配1至多個空格。之后的“,”與年份之間的空格是可 有可無的,所以使用“*”表示為匹配0至多個
三十二、提取月日年
從Email中提取出用戶名和域名,比如從test@163.com中提取出test和163.com。
這個我們就不用說了。
“192.168.10.5[port=21,type=ftp]”,這個字符串表示IP地址為192.168.10.5的服務器的21端口提供的 是ftp服務,其中如果“,type=ftp”部分被省略,則默認為http服務。請用程序解析此字符串,然后打印出“IP地址為***的服務器的*** 端口提供的服務為***”
三十三、提取ip端口號服務類型的正則
接下來我們看下貪婪模式與非貪婪模式
三十四、貪婪模式
三十五、終止貪婪模式
下面我們看一個案例:
三十六、案例1
三十七、案例2
三十八、案例3
三十九、案例3終止貪婪模式
案例:從一個頁面提取所有Email地址,用WebClient,自己動手寫Email群發器。
四十、提取網頁中的email
練習:從網站抓取所有的圖片地址,下載到硬盤:MatchCollection matches = Regex.Matches(content, "border=0\\s*src=\"(.*?)\">");//<img.*?src=(.+)?.*?/>
我們這里用個本地圖片。
四十一、本地下載
我們把它模擬成互聯網上訪問,
四十二、把網站部署在“網絡”上
上面這個小工具已經放到文章上面的資源里面供大家下載。
四十三、下載網絡圖片
下載網絡圖片代碼插入位置:

1 #region 下載網頁圖片 2 WebClient wc = new WebClient(); 3 //先把地址下載下來 4 string html = wc.DownloadString("http://localhost:8080/美女們.htm"); 5 //根據網頁源代碼寫正則 6 MatchCollection matches = Regex.Matches(html,"<img alt=\"\" src=\"(.+)\" />"); 7 //循環一下,看看有沒有匹配。 8 int index = 0; 9 foreach (Match item in matches) 10 { 11 Console.WriteLine(item.Value); 12 //提取好路徑 13 string url = "http://localhost:8080/"+item.Groups[1].Value; 14 //下載 15 wc.DownloadFile(url, @"c:\aa\"+index.ToString()+".jpg"); 16 index++; 17 } 18 //wc.DownloadFile("",""); 19 Console.ReadKey(); 20 #endregion
我們可以試着把網頁上的超鏈接抓取下來。
下面我們看下字符串的替換:
四十四、正則表達式替換
練習2:將連續的-都替換成一個-
234-----234--------------34------55
四十五、練習2答案
隱去手機號碼的中間4位。
四十六、替換手機中間四位
細說,之前在工作的過程中,單位有個客服中心,那些孩子們每天都要打電話,系統里面的電話是全的,但是,要是用系統的導出功能,電話的中間4位就被替換了。當時,我們沒怎么注意,當把電話理出來再打的時候,發現全是廢號了。
苦逼程序員寫出這樣的程序,當時我真想罵娘。但是,轉念想想,要是自己之前用過正則,就會留心去觀察了。
練習1:將一段文本中的MM/DD/YYYY格式的日期轉換為YYYY-MM-DD格式 ,比如“我的生日是05/21/2010耶”轉換為“我的生日是2010-05-21耶”。
四十七、提取組練習
新加:熱心同學提問
新加、兩個$就顯示$2
練習2:給一段文本中匹配到的url添加超鏈接,比如把http://www.test.com替換為<a href="http://www.test.com"> http://www.test.com</a>。參考代碼見備注。因為這個是整體做為一個組,比較特殊,難以理解,先把日期轉換的理解了就 好理解了。
四十八、匹配到的url添加超鏈接練習
四十九、為什么亂碼呢?
因為操作系統保存的utf-8,而在這里我們沒有指定用utf-8,所以就亂了。現在網上都有指定編碼的。
五十、指定編碼了
五十一、指定編碼
當然我們這么寫非常的不規范。
五十二、這樣就不亂碼了
五十三、這樣寫也不亂嗎了
練習1:將文本中連續的空格替換為一個空格.
“hello welcome to China.”→”hello welcome to China”
第一個題跟我們之前做的一模一樣。
練習2:給一個手機號碼:13412345678, 134****5678,用正則表達式Replace()
這個也已經演示過了。
練習3:
zxh@itcast.cn→***@itcast.cn
steve_zhao@163.com→**********@163.com
sk@codeedu.com→**@codeedu.com
這個題按照我們目前的知識做還是有點困難。
把郵箱的前幾位都替換成*
五十四、第一次替換
五十五、用委托這個重載
五十六、轉到定義
五十七、這個類型
五十八、委托類型
要傳個委托變量,什么樣的變量才是委托變量呢?委托其實指向的是一個方法。這個方法必須跟委托匹配,返回值也得跟委托匹配。
五十九、用委托執行方法進行替換
我們通過這個案例,大家看到委托是,委托可以給一個方法變量,用委托才能存方法。
六十、效率不太高的寫法
為了防止用戶用惡意代碼留言,大部分論壇都不允許使用HTML語句做為回帖內容(或者進行了轉義),但是這限制了用戶的發揮,比如用戶無法粘貼超鏈 接、無法粘貼圖片地址,無法把自己發送的文字進行加粗,改變字號。為了解決這個問題,人們設計出了UBB代碼,它提供了有限的、無危害的功能,用戶只要遵 循代碼規則就可以實現用戶想要的功能。 UBB語法:http://baike.baidu.com/view/35.htm?fr=ala0_1_1。
六十一、這就是UBB代碼
發布以后就變成html標簽了。就是在用戶編輯的時候不讓用戶發html標簽,全讓用戶發UBB標簽。
我們可以到http://www.wikipedia.org搜索這ubb。
接下來我們就完成ubb代碼轉換成html代碼。
六十二、畫好界面
六十三、替換代碼
現在再看看這段代碼是不是非常的輕松了呢?
六十四、ubb轉換代碼演示
最后我們再看下敏感詞過濾
接下來我們就做個敏感詞過濾
六十五、畫好界面並添加敏感詞
當我點發帖的時候要驗證一下,有沒有這個詞,要是有這個詞的話,提示一下用戶。
觀察一下我們為大家提供的詞庫,這部分我們已經放到下載里面,請大家下載。
有些詞對應mod,有些詞對應banned,一種需要審核,一種就需要禁止了。
能實現但是效率不高的C#代碼插入位置:

1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.IO; 10 11 namespace _03敏感詞過濾 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 //1.加載敏感詞到集合,分開存儲 20 List<string> listMod = new List<string>(); 21 List<string> listBanned = new List<string>(); 22 private void btnSend_Click(object sender, EventArgs e) 23 { 24 //4.獲取用戶輸入的帖子內容 25 string usrInput = txtContent.Text.Trim(); 26 //5.先判斷用戶輸入的字符串中是否包含{BANNED}的關鍵詞 27 bool b = false; 28 foreach (string item in listBanned) 29 { 30 if (usrInput.Contains(item)) 31 { 32 b = true; 33 break; 34 } 35 } 36 if (b) 37 { 38 MessageBox.Show("您輸入的帖子內容中包含有【禁止發帖】的關鍵詞,請檢查!"); 39 } 40 else 41 { 42 //6.再檢查有沒有需要審核的關鍵詞 43 foreach (string item in listMod) 44 { 45 if (usrInput.Contains(item)) 46 { 47 b = true; 48 break; 49 } 50 } 51 if (b) 52 { 53 MessageBox.Show("您的帖子需要經過審核,請等待!"); 54 } 55 else 56 { 57 MessageBox.Show("發帖成功!"); 58 } 59 } 60 61 } 62 63 private void Form1_Load(object sender, EventArgs e) 64 { 65 //2.當窗體加載的時候,填充集合 66 //把數據讀取到列表里面,處理下編碼的問題 67 string[] line = File.ReadAllLines("1.txt",Encoding.Default); 68 //3.循環判斷分別加入集合 69 foreach (string item in line) 70 { 71 string[] parts = item.Split('='); 72 if (parts[1]=="{MOD}") 73 { 74 //表示該關鍵詞需要審核 75 listMod.Add(parts[0]); 76 } 77 else if (parts[1]=="{BANNED}") 78 { 79 listBanned.Add(parts[0]); 80 } 81 } 82 } 83 } 84 }
六十六、能實現但是效率不高的C#代碼演示
下面我們通過正則實現,
問題是,寫個什么樣的正則才能匹配任意的敏感詞呢?
六十七、本身就是一個正則
通過正則實現敏感詞過濾代碼插入位置:

1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.IO; 10 using System.Text.RegularExpressions; 11 12 namespace _03敏感詞過濾 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 //1.加載敏感詞到集合,分開存儲 21 List<string> listMod = new List<string>(); 22 List<string> listBanned = new List<string>(); 23 private void btnSend_Click(object sender, EventArgs e) 24 { 25 26 #region 通過常規方式實現 27 28 29 ////4.獲取用戶輸入的帖子內容 30 //string usrInput = txtContent.Text.Trim(); 31 ////5.先判斷用戶輸入的字符串中是否包含{BANNED}的關鍵詞 32 //bool b = false; 33 //foreach (string item in listBanned) 34 //{ 35 // if (usrInput.Contains(item)) 36 // { 37 // b = true; 38 // break; 39 // } 40 //} 41 //if (b) 42 //{ 43 // MessageBox.Show("您輸入的帖子內容中包含有【禁止發帖】的關鍵詞,請檢查!"); 44 //} 45 //else 46 //{ 47 // //6.再檢查有沒有需要審核的關鍵詞 48 // foreach (string item in listMod) 49 // { 50 // if (usrInput.Contains(item)) 51 // { 52 // b = true; 53 // break; 54 // } 55 // } 56 // if (b) 57 // { 58 // MessageBox.Show("您的帖子需要經過審核,請等待!"); 59 // } 60 // else 61 // { 62 // MessageBox.Show("發帖成功!"); 63 // } 64 //} 65 #endregion 66 67 #region 通過正則實現 68 string usrInput = txtContent.Text.Trim(); 69 string[] line = File.ReadAllLines("1.txt",Encoding.Default); 70 71 StringBuilder sbMod = new StringBuilder(); 72 StringBuilder sbBanned = new StringBuilder(); 73 foreach (string item in line) 74 { 75 string[] parts = item.Split('='); 76 if (parts[1]=="{MOD}") 77 { 78 sbMod.Append(parts[0]+"|"); 79 } 80 else if (parts[1]=="{BANNED}") 81 { 82 sbBanned.Append(parts[0]+"|"); 83 } 84 } 85 sbMod.Remove(sbMod.Length-1,1); 86 sbBanned.Remove(sbBanned.Length - 1, 1); 87 string s1 = sbMod.ToString(); 88 string s2 = sbBanned.ToString(); 89 90 //直接用正則驗證就行了 91 if (Regex.IsMatch(usrInput,s2)) 92 { 93 MessageBox.Show("包含禁止發帖的關鍵字"); 94 } 95 else if (Regex.IsMatch(usrInput,s1)) 96 { 97 MessageBox.Show("需要審核"); 98 } 99 else 100 { 101 MessageBox.Show("發帖成功"); 102 } 103 104 #endregion 105 } 106 107 private void Form1_Load(object sender, EventArgs e) 108 { 109 //2.當窗體加載的時候,填充集合 110 //把數據讀取到列表里面,處理下編碼的問題 111 string[] line = File.ReadAllLines("1.txt",Encoding.Default); 112 //3.循環判斷分別加入集合 113 foreach (string item in line) 114 { 115 string[] parts = item.Split('='); 116 if (parts[1]=="{MOD}") 117 { 118 //表示該關鍵詞需要審核 119 listMod.Add(parts[0]); 120 } 121 else if (parts[1]=="{BANNED}") 122 { 123 listBanned.Add(parts[0]); 124 } 125 } 126 } 127 } 128 }
如果要是發帖的人這樣在 辦證中間加個特殊符號,我們可以改下詞庫,辦.{0,2}證
面試題:抓取招聘信息。
只抓取職位的超鏈接的href、標題,找職位超鏈接的“模式” http://search.51job.com/job/46629381,c.html,找規律。
使用開發人員工具,可能看到的源代碼不准確。
作者近期文章列表:
C#中級進階教程(完全免費,獻給代碼愛好者的最好禮物。注:本作者分享自己精心整理的C#中級進階教程,無任何商業目的。希望與更多的代碼愛好者交流心得,也請高手多多指點!!!) | |
ASP.net項目 | 圖書商城項目總論 |
三層及其它內容 | 遞歸 |
三層(一) | |
三層相關案例(及常見的錯誤) | |
三層實例(內涵Sql CRUD) | |
手寫代碼生成器 | |
SQL數據庫 ADO.net | 數據庫的應用圖解一 |
數據庫的應用詳解二 | |
ADO.NET(內涵效率問題) | |
ADO.NET實例教學一 | |
ADO.NET實例教學二 | |
數據庫的應用詳解三 | |
ADO.NET(內含存儲過程講解) | |
面向過程,面向對象中高級 | 面向過程,面向對象的深入理解一 |
面向過程,面向對象的深入理解二 | |
面向對象的深入理解三 | |
無處不在的XML | |
winform基礎 | Winform基礎 |
winform中常用的控件 | |
面向過程 | 三種循環的比較 |
C#中的方法(上) | |
我們常見的數組 | |
面向對象 | 思想的轉變 |
C#中超級好用的類 | |
C#中析構函數和命名空間的妙用 | |
C#中超級好用的字符串 | |
C#中如何快速處理字符串 | |
值類型和引用類型及其它 | |
ArrayList和HashTable妙用一 | |
ArrayList和HashTable妙用二 | |
文件管理File類 | |
多態 | |
C#中其它一些問題的小節 | |
GDI+ | 這些年我收集的GDI+代碼 |
這些年我收集的GDI+代碼2 | |
HTML概述以及CSS | 你不能忽視的HTML語言 |
你不能忽視的HTML語言2精編篇 | |
你不能忽視的HTML語言3 | |
html-綜合篇 | |
CSS基本相關內容--中秋特別奉獻 | |
CSS基本相關內容2 | |
JavaScript基礎 | JavaScript基礎一 |
javascript基礎二JavaScript DOM編程 | |
jQuery | jQuery(內涵: jquery選擇器) |
jquery實例教學一 |