計算機專業畢業或者使用過Unix、Perl等產品的讀者一定對正則表達式有一定的印象,即使沒有接觸過正則表達式也不要被它的外貌所嚇倒。雖然做到精通正則表達式比較難,但是能夠掌握它的基本應用卻是非常容易的,一旦把正則表達式應用於實際問題的解決就可以非常明顯的提高工作效率。
爲什麼要用正則表達式
你一定做過字符串解析的工作吧,那麼讓我們再來重溫一下噩夢!
【噩夢1】“192.168.10.5[port=8080]”,這個字符串表示IP地址爲192.168.10.5的服務器的8080端口是打開的,請用程序解析此字符串,然後打印出“IP地址爲***的服務器的***端口是打開的”。
代碼 1.1 普通方法進行IP地址字符串解析
String text = "192.168.10.5[port=8080]";
int leftBraceIndex = text.indexOf('[');
String ipAddr = text.substring(0,leftBraceIndex);
int equalsIndex = text.indexOf('=');
String port = text.substring(equalsIndex+1,text.length()-1);
System.out.println("IP地址爲"+ipAddr+"的服務器的"+port+"端口是打開的");
好在字符串格式不復雜,這麼簡單的任務還是能輕鬆搞定的。
【噩夢2】 “192.168.10.5[port=21,type=ftp]”,這個字符串表示IP地址爲192.168.10.5的服務器的21端口提供的是ftp服務,其中如果“,type=ftp”部分被省略,則默認爲http服務。請用程序解析此字符串,然後打印出“IP地址爲***的服務器的***端口提供的服務爲***”。
代碼 1.2 普通方法進行IP地址字符串解析2
public static void parseAddr2(String text)
{
int leftBraceIndex = text.indexOf('[');
String ipAddr = text.substring(0, leftBraceIndex);
String subStr = text.substring(leftBraceIndex + 1, text.length() - 1);
String[] strings = subStr.split(",");
String portString = strings[0];
String port = portString.split("=")[1];
String type = "http";
if (strings.length == 2)
{
String typeString = strings[1];
type = typeString.split("=")[1];
}
String msg = MessageFormat.format("IP地址爲{0}的服務器的{1}端口提供的服務爲{2}",
new Object[] { ipAddr, port, type });
System.out.println(msg);
}
運行如下的測試代碼:
parseAddr2("192.168.10.5[port=21,type=ftp]");
parseAddr2("192.168.10.5[port=80]");
運行結果:
IP地址爲192.168.10.5的服務器的21端口提供的服務爲ftp
IP地址爲192.168.10.5的服務器的80端口提供的服務爲http
【噩夢3】判斷一個字符串是否是Email?
代碼 1.3 普通方法驗證Email正確性
public static boolean validateEmail(String text)
{
int atIndex = text.indexOf('@');
int dotLastIndex = text.lastIndexOf('.');
//必須含有@和.
if (atIndex < 0 || dotLastIndex < 0)
{
return false;
}
int textLen = text.length();
//不能以@或者.開始或者結束
if (atIndex == 0 || atIndex == textLen || dotLastIndex == 0
|| dotLastIndex == textLen)
{
return false;
}
//@要在最後一個.之前
if (atIndex > dotLastIndex)
{
return false;
}
return true;
}
運行如下的測試代碼:
System.out.println(validateEmail("[email protected]"));
System.out.println(validateEmail("bcdes"));
System.out.println(validateEmail("@cownew.com"));
運行結果:
true
false
false
【噩夢4】從一個文本中提取Email,比如從下面的文本中提取Email地址:
如鵬網的網址是http://www.rupeng.com,下面是幾個開發人員的email:[email protected]、[email protected],其他相關郵件可以發送到:[email protected]。
My God!饒了我吧!
可以看到,如果沒有合適的理論和工具的支持,字符串的解析是一個非常痛苦的過程,像這裏這幾個這麼簡單的例子如果都要通過大量的if……else……語句進行解析,那編譯器解析程序源碼的過程不就會像是比天書還要難懂的代碼了嗎?
做計算機基礎理論的科學家們爲了解決自然語言的識別提出了正則表達式的概念,從而大大簡化了文本的識別工作,並且爲自動機等基礎理論提供了堅實的基礎。現在正則表達式已經從計算機理論的神壇走了下來,我們能在很多工具中找到正則表達式的身影,比如Unix中的vi編輯器、Perl腳本語言等。此外,現在流行的開發語言比如C#、VB、Java等等也都提供了對正則表達式的直接支持,甚至在像JavaScript這種腳本語言中也能發現對正則表達式的支持。正則表達式已經超出了某種語言或某個系統的侷限,成爲被人們廣爲使用的工具,我們完全可以用它來解決實際開發中碰到的一些實際的問題。
元字符 | 說明 |
. | 匹配任何單個字符。例如正則表達式“b.g”能匹配如下字符串:“big”、“bug”、“b g”,但是不匹配“buug”。 |
$ | 匹配行結束符。例如正則表達式“EJB$”能夠匹配字符串“I like EJB”的末尾,但是不能匹配字符串“J2EE Without EJBs!”。 |
^ | 匹配一行的開始。例如正則表達式“^Spring”能夠匹配字符串“Spring is a J2EE framework”的開始,但是不能匹配“I use Spring in my project”。 |
* | 匹配0至多個在它之前的字符。例如正則表達式“zo*”能匹配“z”以及“zoo”;正則表達式“.*”意味着能夠匹配任意字符串。 |
/ | 轉義符,用來將元字符當作普通的字符來進行匹配。例如正則表達式/$被用來匹配美元符號,而不是行尾;正則表達式/.用來匹配點字符,而不是任何字符的通配符。 |
[] | 匹配括號中的任何一個字符。例如正則表達式“b[aui]g”匹配bug、big和bug,但是不匹配beg。可以在括號中使用連字符“-”來指定字符的區間來簡化表示,例如正則表達式[0-9]可以匹配任何數字字符,這樣正則表達式“a[]c”就可以匹配“a0c”、“a1c”、“a2c”等字符串;還可以制定多個區間,例如“[A-Za-z]”可以匹配任何大小寫字母。還有一個相配合使用的元字符“^”,用在這裏並不像前邊的那個“^”一樣表示匹配行開始,而是表示“排除”,要想匹配除了指定區間之外的字符,就可以在左邊的括號和第一個字符之間使用^字符,例如“[^163A-Z]”將能偶匹配除了1、6、3和所有大寫字母之外的任何字符。 |
( ) | 將 () 之間括起來的表達式定義爲“組”(group),並且將匹配這個表達式的字符保存到一個臨時區域,這個元字符在字符串提取的時候非常有用。 |
| | 將兩個匹配條件進行邏輯“或”運算。'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 則匹配"zood" 或 "food"。 |
+ | 匹配前面的子表達式一次或多次。例如正則表達式9+匹配9、99、999等。 |
? | 匹配前面的子表達式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。此元字符還有另外一個用途,就是表示非貪婪模式匹配,後邊將有介紹 |
{n} | 匹配確定的 n 次。例如,“e{2}”不能匹配“bed”中的“d”,但是能匹配“seed”中的兩個“e”。 |
{n,} | 至少匹配n次。例如,“e{2,}”不能匹配“bed”中的“e”,但能匹配“seeeeeeeed”中的所有“e”。 |
{n,m} | 最少匹配 n 次且最多匹配 m 次。“e{1,3}”將匹配“seeeeeeeed”中的前三個“e”。 |