- Java核心技術(shù)·卷Ⅱ:高級特性(原書第10版)
- (美)凱S.霍斯特曼
- 3365字
- 2020-10-30 18:10:46
2.7 正則表達式
正則表達式(regular expression)用于指定字符串的模式,你可以在任何需要定位匹配某種特定模式的字符串的情況下使用正則表達式。例如,我們有一個示例程序就是用來定位HTML文件中的所有超鏈接的,它是通過查找<a href="...">模式的字符串來實現(xiàn)此目的的。
當(dāng)然,在指定模式時,...標(biāo)記法并不夠精確。你需要精確地指定什么樣的字符序列才是合法的匹配,這就要求無論何時,當(dāng)你要描述一個模式時,都需要使用某種特定的語法。
下面是一個簡單的示例,正則表達式

匹配下列形式的所有字符串:
·第一個字母是J或j。
·接下來的三個字母是ava。
·字符串的其余部分由一個或多個任意的字符構(gòu)成。
例如,字符串“javanese”就匹配這個特定的正則表達式,但是字符串“core java”就不匹配。
正如你所見,你需要了解一點這種語法,以理解正則表達式的含義。幸運的是,對于大多數(shù)情況,一小部分很直觀的語法結(jié)構(gòu)就足夠用了。
·字符類(character class)是一個括在括號中的可選擇的字符集,例如,[Jj]、[0-9]、[A-Za-z]或[^0-9]。這里“-”表示是一個范圍(所有Unicode值落在兩個邊界范圍之內(nèi)的字符),而^表示補集(除了指定字符之外的所有字符)。
·如果字符類中包含“-”,那么它必須是第一項或最后一項;如果要包含“[”,那么它必須是第一項;如果要包含“^”,那么它可以是除開始位置之外的任何位置。其中,你只需要轉(zhuǎn)義“[”和“\”。
·有許多預(yù)定的字符類,例如\d(數(shù)字)和\p{Sc}(Unicode貨幣符號)。請查看表2-6和表2-7。
表2-6 正則表達式語法



表2-7 與\p一起使用的預(yù)定義字符類名字

·大部分字符都可以與它們自身匹配,例如在前面示例中的ava字符。
·.符號可以匹配任何字符(有可能不包括行終止符,這取決于標(biāo)志的設(shè)置)。
·使用\作為轉(zhuǎn)義字符,例如,\.匹配句號而\\匹配反斜線。
·^和$分別匹配一行的開頭和結(jié)尾。
·如果X和Y是正則表達式,那么XY表示“任何X的匹配后面跟隨Y的匹配”,X|Y表示“任何X或Y的匹配”。
·你可以將量詞運用到表達式X:X+(1個或多個)、X*(0個或多個)與X?(0個或1個)。
·默認情況下,量詞要匹配能夠使整個匹配成功的最大可能的重復(fù)次數(shù)。你可以修改這種行為,方法是使用后綴?(使用勉強或吝嗇匹配,也就是匹配最小的重復(fù)次數(shù))或使用后綴+(使用占有或貪婪匹配,也就是即使讓整個匹配失敗,也要匹配最大的重復(fù)次數(shù))。
例如,字符串cab匹配[a-z]*ab,但是不匹配[a-z]*+ab。在第一種情況中,表達式[a-z]*只匹配字符c,使得字符ab匹配該模式的剩余部分;但是貪婪版本[a-z]*+將匹配字符cab,模式的剩余部分將無法匹配。
·我們使用群組來定義子表達式,其中群組用括號()括起來。例如,([+-]?)([0-9]+)。然后你可以詢問模式匹配器,讓其返回每個組的匹配,或者用\n來引用某個群組,其中n是群組號(從\1開始)。
例如,下面是一個有些復(fù)雜但是卻可能很有用的正則表達式,它描述了十進制和十六進制整數(shù):

遺憾的是,在使用正則表達式的各種程序和類庫之間,表達式語法并未完全標(biāo)準(zhǔn)化。盡管在基本結(jié)構(gòu)上達成了一致,但是它們在細節(jié)上仍舊存在著許多令人抓狂的差異。Java正則表達式類使用的語法與Perl語言使用的語法十分相似,但是并不完全一樣。表2-6展示的是Java語法中的所有結(jié)構(gòu)。關(guān)于正則表達式語法的更多信息,可以求教于Pattern類的API文檔和Jeffrey E.F.Friedl的《Mastering Regular Expressions》(O’Reilly and Associates,2006)。
正則表達式的最簡單用法就是測試某個特定的字符串是否與它匹配。下面展示了如何用Java來編寫這種測試,首先用表示正則表達式的字符串構(gòu)建一個Pattern對象。然后從這個模式中獲得一個Matcher,并調(diào)用它的matches方法:

這個匹配器的輸入可以是任何實現(xiàn)了CharSequence接口的類的對象,例如String、StringBuilder和CharBuffer。
在編譯這個模式時,你可以設(shè)置一個或多個標(biāo)志,例如:

或者可以在模式中指定它們:

下面是各個標(biāo)志。
·Pattern.CASE_INSENSITIVE或r:匹配字符時忽略字母的大小寫,默認情況下,這個標(biāo)志只考慮US ASCII字符。
·Pattern.UNICODE_CASE或u:當(dāng)與CASE_INSENSITIVE組合使用時,用Unicode字母的大小寫來匹配。
·Pattern.UNICODE_CHARACTER_CLASS或U:選擇Unicode字符類代替POSIX,其中蘊含了UNICODE_CASE。
·Pattern.MULTILINE或m:^和$匹配行的開頭和結(jié)尾,而不是整個輸入的開頭和結(jié)尾。
·Pattern.UNIX_LINES或d:在多行模式中匹配^和$時,只有'\n'被識別成行終止符。
·Pattern.DOTALL或s:當(dāng)使用這個標(biāo)志時,.符號匹配所有字符,包括行終止符。
·Pattern.COMMENTS或x:空白字符和注釋(從#到行末尾)將被忽略。
·Pattern.LITERAL:該模式將被逐字地采納,必須精確匹配,因字母大小寫而造成的差異除外。
·Pattern.CANON_EQ:考慮Unicode字符規(guī)范的等價性,例如,u后面跟隨¨(分音符號)匹配ü。
最后兩個標(biāo)志不能在正則表達式內(nèi)部指定。
如果想要在集合或流中匹配元素,那么可以將模式轉(zhuǎn)換為謂詞:

其結(jié)果中包含了匹配正則表達式的所有字符串。
如果正則表達式包含群組,那么Matcher對象可以揭示群組的邊界。下面的方法

將產(chǎn)生指定群組的開始索引和結(jié)束之后的索引。
可以直接通過調(diào)用下面的方法抽取匹配的字符串:

群組0是整個輸入,而用于第一個實際群組的群組索引是1。調(diào)用groupCount方法可以獲得全部群組的數(shù)量。對于具名的組,使用下面的方法

嵌套群組是按照前括號排序的,例如,假設(shè)我們有下面的模式

和下面的輸出

那么,匹配器會報告下面的群組:

程序清單2-6的程序提示輸入一個模式,然后提示輸入用于匹配的字符串,隨后將打印出輸入是否與模式相匹配。如果輸入匹配模式,并且模式包含群組,那么這個程序?qū)⒂美ㄌ柎蛴〕鋈航M邊界,例如

程序清單2-6 regex/RegexTest.java


通常,你不希望用正則表達式來匹配全部輸入,而只是想找出輸入中一個或多個匹配的子字符串。這時可以使用Matcher類的find方法來查找匹配內(nèi)容,如果返回true,再使用start和end方法來查找匹配的內(nèi)容,或使用不帶引元的group方法來獲取匹配的字符串。

程序清單2-7對這種機制進行了應(yīng)用,它定位一個Web頁面上的所有超文本引用,并打印它們。為了運行這個程序,你需要在命令行中提供一個URL,例如

程序清單2-7 match/HrefMatch.java


Matcher類的replaceAll方法將正則表達式出現(xiàn)的所有地方都用替換字符串來替換。例如,下面的指令將所有的數(shù)字序列都替換成#字符。

替換字符串可以包含對模式中群組的引用:$n表示替換成第n個群組,${name}被替換為具有給定名字的組,因此我們需要用\$來表示在替換文本中包含一個$字符。
如果字符串中包含$和\,但是又不希望它們被解釋成群組的替換符,那么就可以調(diào)用matcher.replaceAll(Matcher.quoteReplacement(str))。
replaceFirst方法將只替換模式的第一次出現(xiàn)。
最后,Pattern類有一個split方法,它可以用正則表達式來匹配邊界,從而將輸入分割成字符串?dāng)?shù)組。例如,下面的指令可以將輸入分割成標(biāo)記,其中分隔符是由可選的空白字符包圍的標(biāo)點符號。

如果有多個標(biāo)記,那么可以惰性地獲取它們:

如果不關(guān)心預(yù)編譯模式和惰性獲取,那么可以使用String.split方法:

java.util.regex.Pattern 1.4
·static Pattern compile(String expression)
·static Pattern compile(String expression,int flags)
把正則表達式字符串編譯到一個用于快速處理匹配的模式對象中。
參數(shù):expression 正則表達式
flags CASE_INSENSITIVE、UNICODE_CASE、MULTILINE、UNIX_LINES、DOTALL和CANON_EQ標(biāo)志中的一個
·Matcher matcher(CharSequence input)
返回一個matcher對象,你可以用它在輸入中定位模式的匹配。
·String[]split(CharSequence input)
·String[]split(CharSequence input,int limit)
·StreamsplitAsStream(CharSequence input)8
將輸入分割成標(biāo)記,其中模式指定了分隔符的形式。返回標(biāo)記數(shù)組,分隔符并非標(biāo)記的一部分。
參數(shù):input 要分割成標(biāo)記的字符串
limit 所產(chǎn)生的字符串的最大數(shù)量。如果已經(jīng)發(fā)現(xiàn)了limit-1個匹配的分隔符,那么返回的數(shù)組中的最后一項就包含所有剩余未分割的輸入。如果limit≤0,那么整個輸入都被分割;如果limit為0,那么墜尾的空字符串將不會置于返回的數(shù)組中。
java.util.regex.Matcher 1.4
·boolean matches()
如果輸入匹配模式,則返回true。
·boolean lookingAt()
如果輸入的開頭匹配模式,則返回true。
·boolean find()
·boolean find(int start)
嘗試查找下一個匹配,如果找到了另一個匹配,則返回true。
參數(shù):start 開始查找的索引位置
·int start()
·int end()
返回當(dāng)前匹配的開始索引和結(jié)尾之后的索引位置。
·String group()
返回當(dāng)前的匹配。
·int groupCount()
返回輸入模式中的群組數(shù)量。
·int start(int groupIndex)
·int end(int groupIndex)
返回當(dāng)前匹配中給定群組的開始和結(jié)尾之后的位置。
參數(shù):groupIndex 群組索引(從1開始),或者表示整個匹配的0
·String group(int groupIndex)
返回匹配給定群組的字符串。
參數(shù):groupIndex 群組索引(從1開始),或者表示整個匹配的0
·String replaceAll(String replacement)
·String replaceFirst(String replacement)
返回從匹配器輸入獲得的通過將所有匹配或第一個匹配用替換字符串替換之后的字符串。
參數(shù):replacement 替換字符串,它可以包含用$n表示的對群組的引用,這時需要用\$來表示字符串中包含一個$符號
·static String quoteReplacement(String str)5.0
引用str中的所有\(zhòng)和$。
·Matcher reset()
·Matcher reset(CharSequence input)
復(fù)位匹配器的狀態(tài)。第二個方法將使匹配器作用于另一個不同的輸入。這兩個方法都返回this。
你現(xiàn)在已經(jīng)看到了在Java中輸入輸出操作是如何實現(xiàn)的,也對正則表達式有了概略的了解。在下一章中,我們將轉(zhuǎn)而研究對XML數(shù)據(jù)的處理。
- Building Modern Web Applications Using Angular
- Mastering JavaScript Object-Oriented Programming
- Arduino by Example
- Rust編程:入門、實戰(zhàn)與進階
- C#編程入門指南(上下冊)
- C和C++安全編碼(原書第2版)
- Getting Started with PowerShell
- DevOps入門與實踐
- Learning SQLite for iOS
- 游戲程序設(shè)計教程
- Windows Server 2012 Unified Remote Access Planning and Deployment
- Java編程技術(shù)與項目實戰(zhàn)(第2版)
- QGIS By Example
- Corona SDK Mobile Game Development:Beginner's Guide(Second Edition)
- 工業(yè)機器人離線編程