「不會正則表達式,就算寫遍代碼也嘛不是」。說到正則表達式,可能動態語言的碼農Perl,Python,JS甚至是Golang的開發人員可能都熟悉。對Java碼農來說,可能CURD手到擒來,Spring Stuts Hibernat耳聞能詳,但是說到Regex RE模式,可能熟練的少。
那麼,今天蟲蟲就來給廣大Java碼農來補補正則的課。
正則基礎
正則表達式(Regex,簡稱RE)是一種根據字符串集中的每個字符串的共同特徵來描述字符串集的方法。可用於搜索,編輯或處理文本和數據。簡單來說,正則表達式是幫助我們根據特定格式驗證或匹配字符串的方式。可以類比資料庫的SQL語言,sql是搜索數據,RE是搜索字符串。正則表達式和SQL語言是開發界的兩個偉大發明。
正則表達式可以用於:
驗證用戶的輸入。
搜索給定數據中的文本。(可在文本編輯器中使用)
編譯器中的解析器
語法突顯,數據包嗅探器等。
要全面了解Regex,我們要理解基本知識,下面我們分別介紹,示例中我們用到在線正則解析網站regex101。
基本量詞(*+?和{n})
正則表示式子中(各語言通用)中的數量詞由*,+.? 和{n}組成。
* 表示匹配零個或多個其先前模式的實例。例如,abc*表示文本必須與'ab'相匹配,後跟零個或多個'c',即文本可能沒有附加'c'並且文本也可能有一個或多個'c'。
+ 匹配其先前模式的一個或多個實例,例如abc+表示文本必須帶有「ab」,後跟一個或多個「c」。所以abc是您可以擁有的至少是正確的,而abcc也是正確的。
?匹配零個或一個出現的模式,例如abc?表示文本可以是abc或ab。
{n}表示與表達式中指定的確切數字(n)匹配。例如a{2}bc表示只能有兩個「a」,然後是一個「bc」和一個「c」。
{n,}至少匹配指定的數字。必須具有n個或更多個前面的表達式,例如ab{2,}c表示必須具有個a,然後是兩個或多個'b',然後是c。
{n,m}表示在模式的n和m(含)之間匹配。這意味著您可以在前至後之間出現m到m個事件。例如ab{2,5}c表示abbc,abbbc,abbbbc,abbbbbc都是正確的。
'.' 匹配所有非空格字符
運算符(|| [] ^和())
|表示「或」。表示與左側的表達式或右側的表達式匹配。例如abc|abd表示文本應為abc或abd。
[]表示文本應與尖括號中的任何字符匹配,例如a[bc]表示文本應為「a」,後跟「b」或「c」。
[0-9]%表示文本應為0到9之間的任何數字,後跟'%'
[a-zA-Z]表示匹配任何一個英文字母,只要介於az或AZ之間即可。
[abc]表示文本或字符串應為a或b或c。
在任何表達式中添加「^」會否定該表達式的含義,例如[^abc]表示匹配任何除abc外的任何字符。
()表示分組,分組後可以在後續反向引用。反向引用存儲與組匹配的字符串部分。可以使用符號$來引用特定的組。$1,$2…代表第一組1,第2組等。默認組為$0,表示字符串本身。例如,我們要刪除行中的所有空格。此示例的正則表達式為以下代碼片段:
private static String backReference(String text) {
String pattern = "(\\w+)([\\s])";
return text.replaceAll(pattern, "$1");
}
上面代碼中的正則表達式有2組:(\\w+)和([\\s])。表示字串是一系列單詞字符(\w+)後跟空格(\s+)。後面一句,我們用組1($1)來替換整個文本,這樣就刪除了空格。
注意:在Java中,我們需要用一個斜槓對字符類(\w和\s)進行轉義,否則會出現語法錯誤。
環視
環視一種排除模式的方法。因此,可以說只有在特定字符串之前沒有改符號時字符串才是有效的,反之亦然。例如,abc(?= d)只要與'd'相符,就會匹配'abc'。 「d」不會被匹配。這叫正向環視或者順序環視。
環視不是所有語言都支持,不是通用的語法,但是妙用可以解決很多問題,比如我們要解析HTML語法:
例如,對html中的一個連接
<a href=』ijz.me』> chognchong</a >
我們要取其中的連接地址
(?=')(.*(?='))
還有一個表示對順序環視的否定表達式,(?!pattern),表示如果'ab'後面沒有'c',則ab(?c)將匹配ab。
注意還有一個逆序環視,java中不支持,我們不介紹。
貪婪和懶惰
正則表達式中的的量詞默認貪婪的,匹配時候會儘可能多的匹配,這樣才能減少遞歸回溯搜索的次數,因而效率最高。例如,對於正則表達式a.+c,希望它表示文本應為'a',後跟一個或多個非空格字符。可能的匹配匹配項為「abcccd」,「abcc」或「abbc」,都是可以的,但是由於貪婪的緣故,它將把所有文本(abcccd abcc abbc)作為一個並返回「abcccd abcc abbc」作為一個結果,因為如果您注意到,第一個字符是「a」,然後是任何其他字符中的一個或多個,它現在以與a.+c完全匹配的c結尾。
為了修改默認的,貪婪模式,只需在量詞前面添加問號(?),這樣使量詞就會只要匹配最少匹配的模式。這時,ab.+?c就會單獨匹配各個字串,而不是整個字符串。
對此的更好應用是:假設您只想從<h1> Chongchong </h1>中單獨獲得標籤<h1>和</h1>,則希望它的正則表達式為<.+>但實際上,它將捕獲整個文本(<h1> Hello Chongchong</h1>)並將其作為一個整體處理。 這時候用?就可以解決
加'?'在量詞前面有時被稱為懶惰模式。
簡而言之,貪婪模式表示匹配儘可能長的字符串,而懶惰模式表示匹配儘可能短的字符串。
字符類
字符類是代表一組字符的轉義序列。下面列出了Java中的一些預定義字符:
\d 表示任意數字;
\s表示空格字符(tab 空格等);
\w 任意單詞字符;
\D表示任意非數字;
\S 表示任意非空格字符;
\W表示任意非單詞字符。
Java中使用正則表達式
好,學習了基礎正則知識後,我們來轉入到Java。來學習在Java中正則表達式的使用。在Java世界Java中的字符串類帶有一個內置的布爾方法,該方法稱為matchs,該方法用來對字符串進行正則匹配。
public static void main(String[] args) {
String value = "12345";
System.out.println("The Result is: "+value.matches("\\d{5}"));
}
上面的代碼段將返回「結果為:true」,因為值(12345)恰好匹配5個字符。除5以外的任何其他值都將返回「結果為:false」。
Pattern/Matcher匹配
除了String類的matchs方法之外,java.util.regex包中也有正則表達式所需的類。它們由三類組成:
Pattern:這是正則表達式的編譯表示。要使用此功能,必須首先在模式類中調用靜態方法(編譯),該方法返回一個模式對象。
Matcher:這是解釋模式並針對輸入字符串執行匹配操作的引擎。要獲得一個對象,必須在Pattern對象上調用matcher方法。
PatternSyntaxException:這表示正則表達式模式中的錯誤
可以使用用模式Pattern/Matcher匹配字符串,一個例子是:
String value = "12345";
String regex = "\\d{5}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(value);
System.out.println("The Result is: "+matcher.matches());
獲取匹配的字符
Matcher支持find和方法lookingAt()獲取匹配的字符,一個例子:
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("aaa2223bb");
m.find();//2223
m.start();//3
m.end();//7,這是2223後的索引號
m.group();//2223
也可以使用lookingAt()方法,只之前的字符串:
Mathcer m2=m.matcher("2223bb");
m.lookingAt(); // 2223
m.start(); // 0,由於lookingAt()只能匹配前面的字符串, start()方法總是返回0
m.end(); // 4
m.group(); // 2223
性能問題
這樣需要每次都先創建 Pattern/Matcher對象耗費資源太大,性能不行。使用Pattern/Matcher是在String類中實現了方法匹配。因此,對於匹配的每個字符串,它都會在匹配之前創建一個Pattern對象。為了解決這個問題,我們可以使用預編譯方法,下面是一個實例:
上面的代碼將數組中的所有元素與特定的正則表達式([a-zA-Z]*log[a-zA-Z]*)進行匹配。正則表達式用來獲取所有帶有「log」一詞的文本。在與正則表達式匹配之前,必須先編譯該表達式(Pattern.compile);
第一個方法matchUsingPatternMatcher()在尋找匹配項之前先編譯模式,而第二個方法matchUsingStringMatches()創建一個新的模式對象
數組中每個元素的鏡像(Pattern.compile())操作比較耗費內存,並且當數據/文本過多時,還可能會導致內存洩漏
因此,如果在處理大量數據比較關心性能時,請使用Pattern/Matcher類先進行編譯,然後使用實例來匹配文本。