在程序中常常設計字符串的處理,比如①:判斷用戶的輸入字符串是否符合要求,是否是非法字符串,②:取出一個很復雜字符串的某一程序中需要的部分等
這事用自己寫算法判斷通常是十分困難的,所以遇到字符串的處理時要很快想到用正則表達式。
一:正則表達式元字符
•要想學會正則表達式,理解元字符是一個必須攻克的難關。不用刻意記
•.:匹配任何單個字符。例如正則表達式“b.g”能匹配如下字符串:“big”、“bug”、“b g”,但是不匹配“buug”,“b..g”可以匹配“buug”。
•[ ] :匹配括號中的任何一個字符。例如正則表達式“b[aui]g”匹配bug、big和bag,但是不匹配beg、baug。可以在括號中使用連字符“-”來指定字符的區間來簡化表示,例如正則表達式[0-9]可以匹配任何數字字符,這樣正則表達式“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"。
•*:匹配0至多個在它之前的子表達式,和通配符*沒關系。例如正則表達式“zo*”能匹配 “z” 、“zo”以及 “zoo”;因此“.*”意味着能夠匹配任意字符串。"z(b|c)*"→zb、zbc、zcb、zccc、zbbbccc。"z(ab)*"能匹配z、zab、zabab(用括號改變優先級)。
•+ :匹配前面的子表達式一次或多次,和*對比(0到多次)。例如正則表達式9+匹配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”。
•^(shift+6) :匹配一行的開始。例如正則表達式“^regex”能夠匹配字符串“regex我會用”的開始,但是不能匹配“我會用regex”。
•^另外一種意思:非!(暫時不用理解)
•$ :匹配行結束符。例如正則表達式“浮雲$” 能夠匹配字符串“一切都是浮雲”的末尾,但是不能匹配字符串“浮雲呀”
•正則表達式是與語言無關真的就是想表達\d。
注意這些簡寫表達式是不考慮轉義符的,這里的\就表示字符\,而不是C#字符串級別的\,在C#代碼中需要使用@或者\雙重轉義。區分C#級別的轉移和正則表達式級別的轉移,恰好C#的轉義符和正則表達式的轉義符都是\而已。正則表達式的轉移是在C#之后的(層層盤剝)。把C#的轉義符想成%就明白了。在C#看來@"\-"就是\-這個普通的字符串,只不過在
二、.Net中的正則表達式
1.字符串的匹配:
•正則表達式在.Net就是用字符串表示,這個字符串格式比較特殊,無論多么特殊,在C#語言看來都是普通的字符串,具體什么含義由Regex類內部進行語法分析。
•正則表達式(Regular Expression)的主要類:Regex.IsMatch方法用於判斷一個字符串是否匹配正則表達式。
•字符串匹配例子:
Regex.IsMatch("bbbbg","^b.*g $");
Regex.IsMatch("bg", "^b.*g $ ");
Regex.IsMatch("gege", "^b.*g $ ");
一定不能忘了^和$,否則也能匹配yesbagit
例如:
•判斷是否是合法的郵政編碼(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}”也是正確的。不要忘了@或者\\d
•判斷一個字符串是不是身份證號碼,即是否是15或18位數字。
•錯誤寫法:Regex.IsMatch("123456789123456789", @"^\d{15}|\d{18}$"),表示15位數字開頭或者18位數字結尾。
•正確寫法:Console.WriteLine(Regex.IsMatch("0111111111111111", @"^\d{15}$|^\d{18}$"))或者@"^(\d{15}|\d{18})$"
•判斷字符串是否為正確的國內電話號碼,不考慮分機。比如“010-95555”、“01095555”、“95555”都是正確的號碼。區號為3位或者4位。
• Regex.IsMatch("123456-95555", @"^\d{3,4}\-?\d+$")
•"^\d{3,4}\-?\d+$"表示被匹配的字符序列應該是由三至四位數字組成,由於長途區號的連字符“-”可有可無,所以這里使用“?”元字符進行說明。由於連字符“-”在正則表達式中有特殊含義(表示范圍,比如[0-9]),所以要對其進行轉義。
•判斷一個字符串是否是合法的Email地址。一個Email地址的特征就是以一個字符序列開始,后邊跟着“@”符號,后邊又是一個字符序列,后邊跟着符號“.”,最后是字符序列
•Regex.IsMatch("email12@mail.com", @"^\w+@\w+\.\w+$");
•[]括號中的任意字符,\w字母、數字、下划線,+一到多個。由於.在正則表達式中有特殊的含義,因此對於真正想表達“.”則需要轉移“\.”。先想正則表達式是語言無關的。這是因為C#中、正則表達式中\都代表轉義符,就有點亂了。考慮的時候先不考慮C#語言,分成兩步先考慮正則表達式語法,再考慮找個正則表達式在C#中如何表示。
•取巧的辦法:從ASP.Net的RegularExpressionValidator中抄常用的正則表達式,工作中一般是從網上找現成的。
2.字符串提取:
•正則表達式還可以用來進行字符串提取
Match match = Regex.Match("age=30", @"^(. +)=(.+)$");
if (match.Success)
{
Console.WriteLine(match.Groups[1] .Value);
Console.WriteLine(match.Groups[2] .Value);
}
match的Success屬性表示是否匹配成功;正則表達式中用()將要提取的內容括起來,然后就可以通過Match的Groups屬性來得到所有的提取元素,注意Groups的序號是從1開始的,0有特殊含義
貪婪模式與非貪婪模式
•從文本提取出名字:
•Match match = Regex.Match("大家好。我是S.H.E。我22歲了。我病了,嗚嗚。fffff", "我是(.+)。");//沒有加^$。
•看結果。+、*的匹配默認是貪婪(greedy)的:盡可能多的匹配,直到“再貪婪一點兒”其后的匹配模式就沒法匹配為止。
•在+、*后添加?就變成非貪婪模式(? 的另外一個用途):讓其后的匹配模式盡早的匹配。修改成"我是(.+?)。"
•一般開發的時候不用刻意去修飾為非貪婪模式,只有遇到bug的時候發現是貪婪模式的問題再去解決。因為貪婪效率高。
匹配組
•正則表達式可以從一段文本中將所有符合匹配的內容都輸出出來。Match獲得的是匹配的第一個。Regex.Matches方法可以獲得所有的匹配項。注意區別:匹配和group的區別:見備注1
•練習:從一段文本中提取所有的數字
MatchCollection matches = Regex.Matches("大家好,我是Hebe,我22歲了,身高180,我們團隊有3個女女!", @"\d+");
foreach(Match match in matches)
{Console.WriteLine(match.Value);}
3.C#字符串提取示例代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace 正則表達式取括號里面的值
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//使用正則表達式將,字符串中的需要的數據取出來
//本例去除host,port,已經sid的值
private void button1_Click(object sender, EventArgs e)
{
string host;
string PORT;
string SID;
string str = "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = ahnu)(PORT = 1521)))(CONNECT_DATA = (SID =orcl)))";
str = str.Trim();
//方法一:一個一個取,有針對性,
Match match = Regex.Match(str, @"\(HOST=?(.+?)\)");
if (match.Success)
{
host = match.Groups[1].Value;
}
match = Regex.Match(str, @"\(PORT=?(.+?)\)");
if (match.Success)
{
PORT = match.Groups[1].Value;
}
match = Regex.Match(str, @"\(SID=?(.+?)\)");
if (match.Success)
{
SID = match.Groups[1].Value;
}
//方法二:取出所有的匹配字符串,這里只是介紹如果多個字符串匹配如何處理。
MatchCollection mts = Regex.Matches(str, @"\(HOST(.+?)=?(.+?)\)");
for (int i = 0; i < mts.Count; i++)
{
Match mt = mts[i];
MessageBox.Show(mt.Value);
MessageBox.Show(mt.Groups[1].Value);
}
//方法三:先取出一部分然后作為新的字符串再提取,這樣做避免正則表達式的部分寫錯而匹配不了 情況。
Match m = Regex.Match(str, @".+?=.+?=.+?=(.+?)\)\).+?=(.+?)\)\)");
if (m.Success)
{
string s1 = m.Groups[1].Value;//" (PROTOCOL = TCP)(HOST = AHNU)(PORT = 1521"
string s2 = m.Groups[2].Value;// (SID =ORCL"
if (m.Success)
{
string str3 = m.Groups[1].Value;
string str4 = m.Groups[2].Value; //" (PROTOCOL = TCP)(HOST = AHNU)(PORT = 1521"
if (m.Success)
{
host = m.Groups[1].Value;
PORT = m.Groups[2].Value;
}
m = Regex.Match(s2, @"\(.+?=(.+)");
if (m.Success)
{
SID = m.Groups[1].Value;
}
}
}
}
}
}