java中使用正則表達式

計算機專業畢業或者使用過Unix、Perl等產品的讀者一定對正則表達式有一定的印象,即使沒有接觸過正則表達式也不要被它的外貌所嚇倒。雖然做到精通正則表達式比較難,但是能夠掌握它的基本應用卻是非常容易的,一旦把正則表達式應用於實際問題的解決就可以非常明顯的提高工作效率。

正則表達式最早是由數學家Stephen Kleene在對自然語言的遞增研究成果的基礎上提出來的,具有完整語法的正則表達式使用在字符的格式匹配方面上。正則表達式是一種描述文字模式的方法,包括重複出現、不同選擇方式以及爲某些字符(比如數字、字母)而提供的縮寫形式。我們經常會碰到甚至用到它,在Windows的搜索界面中輸入“*.txt”就可以搜索到所有的文本文件,在DOS提示符下輸入“Dir *.exe”就可以顯示出當前路徑下所有的可執行文件,在數據庫中檢索所有名稱中含有“tom”的記錄就可以執行如下SQL:“select * from T_Table where FName like ‘%tom%’”,這些“*.txt”、“%tom%”就是一種模式,這些模式將會與一些文本相匹配。

爲什麼要用正則表達式

你一定做過字符串解析的工作吧,那麼讓我們再來重溫一下噩夢!

【噩夢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#VBJava等等也都提供了對正則表達式的直接支持,甚至在像JavaScript這種腳本語言中也能發現對正則表達式的支持。正則表達式已經超出了某種語言或某個系統的侷限,成爲被人們廣爲使用的工具,我們完全可以用它來解決實際開發中碰到的一些實際的問題。

 

1.2正則表達式入門
一個正則表達式就是由普通字符(例如大小寫字母)以及特殊字符(稱爲元字符)組成的文字模式。該模式描述在查找文字主體時待匹配的一個或多個字符串。正則表達式作爲一個模板,將某個字符模式與所搜索的字符串進行匹配。
在最簡單的情況下,一個正則表達式看上去就是一個普通的查找串。例如,正則表達式"java"中沒有包含任何元字符,它可以匹配"java"和"javascript"等字符串,但是不能匹配"Java"。
1.2.1正則表達式語法
要想學會正則表達式,理解元字符是一個必須攻克的難關,這裏先給出常用元字符的語法,如表1.1所示。即使不能完全看懂也沒有關係,我們會在後邊通過更多例子幫助讀者理解。
表 1.1 正則表達式語法
元字符 說明
. 匹配任何單個字符。例如正則表達式“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”。
1.2.2 Java中的正則表達式API
在JDK1.3及之前的JDK版本中並沒有包含正則表達式的類,如果要在Java中使用正則表達式必須使用第三方提供的正則表達式庫,最有名的就是Jakarta-ORO,Jakarta-ORO庫以前叫做OROMatcher,是Daniel Savarese贈送給Jakarta Project的一個開源包。
使用的時候首先要創建一個實現了PatternCompiler接口的實例變量以創建一個“模式編譯器”,Jakarta-ORO中實現了這個接口的類就是Perl5Compiler,這個類做到了與Perl5的正則表達式完全兼容。
PatternCompiler compiler=new Perl5Compiler();
接着調用“模式編譯器”的compile方法編譯正則表達式:
Pattern pattern= compiler.compile("b[iue]g ");
接着創建一個實現了PatternMatcher接口的實例變量以創建一個“模式匹配器”,Jakarta-ORO中實現了這個接口的類就是Perl5Matcher:
PatternMatcher matcher=new Perl5Matcher();
然後就可以調用PatternMatcher 接口中的contains、matches等方法來進行模式匹配了。
 
Jakarta-ORO的使用是非常簡便的,而且效率非常高,支持的正則表達式語法也是非常全的,唯一的缺點就是它不是JDK中的標準包。從JDK1.4開始提供了支持正則表達式API,它們位於java.util.regex包中,由於已經有了標準API,所以本書將會用java.util.regex進行正則表達式的相關操作。
java.util.regex中定義了一些表達式的簡寫,可以使得表達式顯得更加簡潔清晰:
/t:製表符,等同於/u0009
/n:換行符,等同於/u000A
/d:代表一個數字,等同於[0-9]
/D:代表非數字,等同於[^0-9]
/s:代表換行符、Tab製表符等空白字符
/S:代表非空白字符
/w:字母字符,等同於[a-zA-Z_0-9]
/W:非字母字符,等同於[^/w]
1.2.3java.util.regex的使用
java.util.regex中包括了兩個類:Pattern和Matcher,與Jakarta-ORO不同的是這兩個類都是終態類,並且沒有定義相關接口。一個Pattern對象就是一個正則表達式經編譯後的表現模式,也就是前邊所說的“模式編譯器”;一個Matcher對象是一個狀態機器,它根據Pattern對象做爲匹配模式對字符串展開匹配檢查,也就是前邊說的“模式匹配器”。因爲模式的編譯過程是最先進行且與匹配過程獨立的,所以這保證了進行批量字符串匹配時候的運行效率。
Pattern類有如下重要的方法:
(1)public static Pattern compile(String regex):將給定的正則表達式編譯並返回編譯後的Pattern對象。
(2)public static Pattern compile(String regex, int flags):將給定的正則表達式編譯並返回編譯後的Pattern對象,flag參數表示匹配時的選項,可選的flag參數包括:CASE_INSENSITIVE,COMMENTS,MULTILINE,DOTALL,UNICODE_CASE,CANON_EQ。 
(3)public int flags():返回flag選項參數.
(4)public static boolean matches(String regex, CharSequence input):直接判斷字符序列input是否匹配正則表達式regex。前面曾經提到當需要使用一個正則表達式進行多次匹配的時候,對正則表達式進行預編譯能夠加快運行速度,但是如果這個匹配只進行一次的話,就可以調用這個matches方法直接判斷是否匹配,這樣就方便多了。這段代碼內部的實現代碼就是:Pattern.compile(regex).matcher(input).matches();
Matcher 類有如下重要的方法:
(1)public boolean matches():生成一個給定命名的Matcher對象 
(2)public String pattern():返回該Pattern對象所編譯的正則表達式。 
(3)public String[] split(CharSequence input)
將目標字符序列input按照Pattern裏所包含的正則表達式爲模式進行分割。 
(4)public String[] split(CharSequence input, int limit) 將目標字符序列input按照Pattern裏所包含的正則表達式爲模式進行分割,參數limit作用是指定要分割的段數,如將limi設爲2,那麼目標字符序列將根據正則表達式分爲割爲兩段。
(5)public String group(int group) :得到匹配結果中提取的第group個分組的值。此方法在字符串提取中經常用到。
(6)public String replaceAll(String replacement):用指定的字符串替換匹配的子串。
 
下面看一下簡單的正則表達式的例子。
【例1-1】 簡單的字符匹配
代碼 1.4 簡單的正則表達式字符匹配
Pattern p = Pattern.compile("b*g");
Matcher m = p.matcher("bbbbbbg");
boolean b = m.matches();
System.out.println(b);
運行結果爲:
true
因爲“b*g”匹配的字符0至多個“b”後面有一個“g”,所以“bbbbbbg”匹配。
這裏採用簡化形式,並匹配另外一個字符串:
boolean b = Pattern.matches("b*g","bug");
System.out.println(b);
運行結果爲:
false
正則表達式不僅能進行字符串的匹配,還能進行字符串的提取、替換。下面就演示一個字符串提取的例子:
【例1-2】從文件路徑中提取出文件名(包含後綴)
比如從“c:/dir1/dir2/name.txt”中提取出“name.txt”。
代碼 1.5 字符串提取
String regEx = ".+/(.+)$";
String str = "c:/dir1/dir2/name.txt";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
if (!m.find())
{
    System.out.println("文件路徑格式錯誤!");
    return;
}
System.out.println(m.group(1));
運行結果:name.txt
正則表達式“.+/(.+)$”的含義就是:被匹配的字符串以任意字符序列開始,後邊緊跟着字符“/”,最後以任意字符序列結尾,“()”代表分組操作,這裏就是把文件名做爲分組,匹配完畢我們就可以通過Matcher 類的group方法取到我們所定義的分組了。需要注意的這裏的分組的索引值是從1開始的,所以取第一個分組的方法是m.group(1)而不是m.group(0)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章