正則表達式乍一看上去是一堆繁瑣的解孙、無規(guī)律的坑填、令人頭大的字符串,但是當你了解他之后妆距,你就會知道它的強大穷遂。很多程序設(shè)計語言都支持用正則表達式來進行字符串的操作函匕。
一娱据、初識正則表達式(Regular Expression)
正則表達式是使用單個字符串來描述、匹配一系列規(guī)則的字符串盅惜。正則表達式并不僅限于某一種語言中剩,在每種語言中有細微的差別。
一個正則表達式字符串:
^p[a-zA-Z_].+@.+\\..+$
二抒寂、正則表達式的應用
正則表達式應用還是很多的结啼。比如我們常用來驗證電話號碼格式、郵箱格式等涉及到有一定格式的數(shù)據(jù)驗證操作屈芜;比如使用正則表達式找到指定字符并進行刪除郊愧、替換等操作朴译;比如在一個字符串中按照正則表達式的條件,找到子串属铁,提取位置等眠寿。
三、正則表達式常用語法
^
匹配輸入字符串開始的位置焦蘑。如果設(shè)置了 RegExp 對象的 Multiline 屬性盯拱,^ 還會與"\n"或"\r"之后的位置匹配。
$
匹配輸入字符串結(jié)尾的位置例嘱。如果設(shè)置了 RegExp 對象的 Multiline 屬性狡逢,$ 還會與"\n"或"\r"之前的位置匹配。
*
零次或多次匹配前面的字符或子表達式拼卵。例如奢浑,zo* 匹配"z"和"zoo"。* 等效于 {0,}腋腮。
+
一次或多次匹配前面的字符或子表達式殷费。例如,"zo+"與"zo"和"zoo"匹配低葫,但與"z"不匹配详羡。+ 等效于 {1,}。
?
零次或一次匹配前面的字符或子表達式嘿悬。例如实柠,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}善涨。
{n}
n 是非負整數(shù)窒盐。正好匹配 n 次。例如钢拧,"o{2}"與"Bob"中的"o"不匹配蟹漓,但與"food"中的兩個"o"匹配。
{n,}
n 是非負整數(shù)源内。至少匹配 n 次葡粒。例如,"o{2,}"不匹配"Bob"中的"o"膜钓,而匹配"foooood"中的所有 o嗽交。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"颂斜。
{n,m}
M 和 n 是非負整數(shù)夫壁,其中 n <= m。匹配至少 n 次沃疮,至多 m 次盒让。例如梅肤,"o{1,3}"匹配"fooooood"中的頭三個 o。'o{0,1}' 等效于 'o?'邑茄。注意:您不能將空格插入逗號和數(shù)字之間凭语。
?
當此字符緊隨任何其他限定符(*、+撩扒、?似扔、{n}、{n,}搓谆、{n,m})之后時炒辉,匹配模式是"非貪心的"。"非貪心的"模式匹配搜索到的泉手、盡可能短的字符串黔寇,而默認的"貪心的"模式匹配搜索到的、盡可能長的字符串斩萌。例如缝裤,在字符串"oooo"中,"o+?"只匹配單個"o"颊郎,而"o+"匹配所有"o"憋飞。
.
匹配除"\r\n"之外的任何單個字符。若要匹配包括"\r\n"在內(nèi)的任意字符姆吭,請使用諸如"[\s\S]"之類的模式榛做。
(pattern)
匹配 pattern 并捕獲該匹配的子表達式∧诶辏可以使用 $0…$9 屬性從結(jié)果"匹配"集合中檢索捕獲的匹配检眯。若要匹配括號字符 ( ),請使用"\("或者"\)"昆淡。
(?:pattern)
匹配 pattern 但不捕獲該匹配的子表達式锰瘸,即它是一個非捕獲匹配,不存儲供以后使用的匹配昂灵。這對于用"or"字符 (|) 組合模式部件的情況很有用避凝。例如,'industr(?:y|ies) 是比 'industry|industries' 更經(jīng)濟的表達式倔既。
(?=pattern)
執(zhí)行正向預測先行搜索的子表達式恕曲,該表達式匹配處于匹配 pattern 的字符串的起始點的字符串鹏氧。它是一個非捕獲匹配渤涌,即不能捕獲供以后使用的匹配。例如把还,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows"实蓬,但不匹配"Windows 3.1"中的"Windows"茸俭。預測先行不占用字符,即發(fā)生匹配后安皱,下一匹配的搜索緊隨上一匹配之后调鬓,而不是在組成預測先行的字符后。
(?!pattern)
執(zhí)行反向預測先行搜索的子表達式酌伊,該表達式匹配不處于匹配 pattern 的字符串的起始點的搜索字符串腾窝。它是一個非捕獲匹配,即不能捕獲供以后使用的匹配居砖。例如虹脯,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"奏候。預測先行不占用字符循集,即發(fā)生匹配后,下一匹配的搜索緊隨上一匹配之后蔗草,而不是在組成預測先行的字符后咒彤。
x|y
匹配 x 或 y。例如咒精,'z|food' 匹配"z"或"food"镶柱。'(z|f)ood' 匹配"zood"或"food"。
[xyz]
字符集模叙。匹配包含的任一字符奸例。例如,"[abc]"匹配"plain"中的"a"向楼。
[^xyz]
反向字符集查吊。匹配未包含的任何字符。例如湖蜕,"[^abc]"匹配"plain"中"p"逻卖,"l","i"昭抒,"n"评也。
[a-z]
字符范圍。匹配指定范圍內(nèi)的任何字符灭返。例如盗迟,"[a-z]"匹配"a"到"z"范圍內(nèi)的任何小寫字母。
[^a-z]
反向范圍字符熙含。匹配不在指定的范圍內(nèi)的任何字符罚缕。例如,"[^a-z]"匹配任何不在"a"到"z"范圍內(nèi)的任何字符怎静。
\b
匹配一個字邊界邮弹,即字與空格間的位置黔衡。例如,"er\b"匹配"never"中的"er"腌乡,但不匹配"verb"中的"er"盟劫。
\B
非字邊界匹配。"er\B"匹配"verb"中的"er"与纽,但不匹配"never"中的"er"侣签。
\cx
匹配 x 指示的控制字符。例如急迂,\cM 匹配 Control-M 或回車符硝岗。x 的值必須在 A-Z 或 a-z 之間。如果不是這樣袋毙,則假定 c 就是"c"字符本身型檀。
\d
數(shù)字字符匹配。等效于 [0-9]听盖。
\D
非數(shù)字字符匹配胀溺。等效于 [^0-9]。
\f
換頁符匹配皆看。等效于 \x0c 和 \cL仓坞。
\n
換行符匹配。等效于 \x0a 和 \cJ腰吟。
\r
匹配一個回車符。等效于 \x0d 和 \cM灵疮。
\s
匹配任何空白字符,包括空格、制表符、換頁符等舌稀。與 [ \f\n\r\t\v] 等效剔应。
\S
匹配任何非空白字符。與 [^ \f\n\r\t\v] 等效。
\t
制表符匹配。與 \x09 和 \cI 等效。
\v
垂直制表符匹配箱吕。與 \x0b 和 \cK 等效雅采。
\w
匹配任何字類字符宝鼓,包括下劃線。與"[A-Za-z0-9_]"等效邑雅。
\W
與任何非單詞字符匹配捧书。與"[^A-Za-z0-9_]"等效。
\xn
匹配 n骤星,此處的 n 是一個十六進制轉(zhuǎn)義碼经瓷。十六進制轉(zhuǎn)義碼必須正好是兩位數(shù)長。例如洞难,"\x41"匹配"A"舆吮。"\x041"與"\x04"&"1"等效。允許在正則表達式中使用 ASCII 代碼队贱。
\num
匹配 num色冀,此處的 num 是一個正整數(shù)。到捕獲匹配的反向引用露筒。例如呐伞,"(.)\1"匹配兩個連續(xù)的相同字符。
\n
標識一個八進制轉(zhuǎn)義碼或反向引用慎式。如果 \n 前面至少有 n 個捕獲子表達式伶氢,那么 n 是反向引用。否則瘪吏,如果 n 是八進制數(shù) (0-7)癣防,那么 n 是八進制轉(zhuǎn)義碼。
\nm
標識一個八進制轉(zhuǎn)義碼或反向引用掌眠。如果 \nm 前面至少有 nm 個捕獲子表達式蕾盯,那么 nm 是反向引用。如果 \nm 前面至少有 n 個捕獲蓝丙,則 n 是反向引用级遭,后面跟有字符 m。如果兩種前面的情況都不存在渺尘,則 \nm 匹配八進制值 nm挫鸽,其中 n 和 m 是八進制數(shù)字 (0-7)。
\nml
當 n 是八進制數(shù) (0-3)鸥跟,m 和 l 是八進制數(shù) (0-7) 時丢郊,匹配八進制轉(zhuǎn)義碼 nml盔沫。
\un
匹配 n,其中 n 是以四位十六進制數(shù)表示的 Unicode 字符枫匾。例如架诞,\u00A9 匹配版權(quán)符號 (?)。
\
將下一字符標記為特殊字符干茉、文本谴忧、反向引用或八進制轉(zhuǎn)義符。例如等脂,"n"匹配字符"n"俏蛮。"\n"匹配換行符撑蚌。序列"\\"匹配"\"上遥,"\("匹配"("。
四争涌、常用的Java正則表達式API:Pattern
Pattern
只要你用到正則表達式粉楚,都是從這個類開始的。典型的調(diào)用順序就是:
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
1.Pattern.matches()
在僅使用一次正則表達式時亮垫,可以方便的通過類方法 matches 實現(xiàn)是否匹配模软。他底層實現(xiàn)也是上面的調(diào)用順序,等效于上面的三個語句饮潦。
boolean b = Pattern.matches("a*b", "aaaaab");
2. Pattern.compile()
如果需要匹配一個正則表達式在文本中多次出現(xiàn)燃异,需要通過Pattern.compile() 方法創(chuàng)建一個Pattern對象,如上面舉例所示。也可以在Compile 方法中继蜡,指定一個特殊標志:
Pattern pattern = Pattern.compile("a*B", Pattern.CASE_INSENSITIVE);
常用標志模式舉例:
UNIX_LINES:啟用Unix模式回俐,在此模式中,.稀并、^ 和 $ 的行為中僅識別 '\n' 行結(jié)束符仅颇。
CASE_INSENSITIVE:啟用不區(qū)分大小寫的匹配。
MULTILINE:啟用多行模式碘举。
COMMENTS :此模式將忽略空白和在結(jié)束行之前以 # 開頭的嵌入式注釋忘瓦。
Pattern 類包含多個標志(int 類型),這些標志可以控制Pattern 匹配模式的方式。常與flags 方法配合使用引颈。
3. Pattern.matcher()
獲得Patterm對象后耕皮,通過要匹配的字符序列來創(chuàng)建匹配器。Matcher類有一個matches()方法蝙场,可以檢查文本是否匹配模式凌停。
Pattern.matches(regex, input);
Pattern.compile(regex).matcher(input).matches()
兩個表達式是等價的。
4. Pattern.split()
用正則表達式做為分隔符李丰,拆分給定的字符序列苦锨,返回一個字符數(shù)組。
String text = "boo:and:foo";
String patternString = ":";
Pattern pattern = Pattern.compile(patternString);
String[] split = pattern.split(text);
System.out.println("split.length = " + split.length);
for(String element : split){
System.out.println("element = " + element);
}
// split.length = 3
// element = boo
// element = and
// element = foo
也可以再加一個int 類型參數(shù)限制拆分次數(shù),從而影響結(jié)果數(shù)組的長度舟舒。
Regex | Limit | Result |
---|---|---|
: | 2 | { "boo", "and:foo" } |
: | 5 | { "boo", "and", "foo" } |
: | -2 | { "boo", "and", "foo" } |
o | 5 | { "b", "", ":and:f", "", "" } |
o | -2 | { "b", "", ":and:f", "", "" } |
o | 0 | { "b", "", ":and:f" } |
Matcher
創(chuàng)建匹配器后拉庶,可以使用它執(zhí)行三種不同的匹配操作:
- matches 方法嘗試將整個輸入序列與該模式匹配。
- lookingAt 嘗試將輸入序列從頭開始與該模式匹配秃励。
- find 方法掃描輸入序列以查找與該模式匹配的下一個子序列氏仗。
1. Matcher. matches()
matches() 方法不能用于查找正則表達式多次出現(xiàn)。如果需要夺鲜,請使用find(), start() 和 end() 方法皆尔。
2. Matcher. lookingAt()
與 matches 方法類似,此方法始終從區(qū)域的開頭開始币励;與之不同的是慷蠕,它不需要匹配整個區(qū)域。如果匹配成功食呻,則可以通過 start流炕、end 和 group 方法獲取更多信息。
String text = "bOO:and:foo";
String patternString1 = "Boo";
Pattern pattern1 = Pattern.compile(patternString1, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern1.matcher(text);
System.out.println("lookingAt = " + matcher.lookingAt());
System.out.println("matches = " + matcher.matches());
// lookingAt = true
// matches = false
**3. Matcher. find()仅胞、start() 每辟、end() **
- find() 方法用于匹配序列中出現(xiàn)的正則表達式,如果在文本中多次匹配干旧,find 方法返回第一個渠欺,之后每次調(diào)用 find 都會返回下一個。
- start()返回匹配序列的初始索引椎眯。
- end() 返回最后匹配字符之后的偏移量挠将。
String text3 = "When you feel like quitting, think about why you started.";
String patternString2 = "ou";
Pattern pattern2 = Pattern.compile(patternString2);
Matcher matcher2 = pattern2.matcher(text3);
int count = 0;
while (matcher2.find()) {
count++;
System.out.println("found: " + count + " : " + matcher2.start() + " - " + matcher2.end());
}
// found: 1 : 6 - 8
// found: 2 : 37 - 39
// found: 3 : 46 - 48
4. Matcher. reset()
reset 方法會重置Matcher 內(nèi)部的所有狀態(tài)信息,匹配區(qū)域被重新設(shè)置成整個字符序列盅视。當find 方法開始匹配時,Matcher 內(nèi)部會記錄截至當前查找的相關(guān)信息捐名。調(diào)用 reset 會重新從文本開頭查找。
也可以調(diào)用 reset(CharSequence) 方法. 這個方法重置Matcher,同時把一個新的字符串作為參數(shù)傳入闹击,用于代替創(chuàng)建 Matcher 的原始字符串镶蹋。
5. Matcher. group()
組、捕獲組是一個很重要的概念赏半。比如我們想提取一個匹配序列中的某一段贺归,使用組的概念就比使用start 方法和end 方法更容易些。
(group)
這就是一個組的語法断箫,當正則表達式匹配到文本后拂酣,可以使用group 方法訪問組內(nèi)的部分。一對括號就標記了一個組仲义,可以有多個分組婶熬。
它返回由以前匹配操作所匹配的輸入子序列剑勾。想要訪問某分組匹配的文本,可以把分組編號傳入 group(int groupNo)方法赵颅。但要注意0是表示整個組虽另,分組是從1開始的。
String text5 = "You cannot improve your past, but you can improve your future. Once time is wasted, life is wasted.";
String patternString5 = "(improve)";
Pattern pattern5 = Pattern.compile(patternString5);
Matcher matcher5 = pattern5.matcher(text5);
while(matcher5.find()) {
System.out.println("found: " + matcher5.group(1));
}
// found: improve
// found: improve
split和組結(jié)合使用饺谬,進行拆分捂刺。
String string2 = "123&&345%%567@@@@085555508";
String[] string3 = string2.split("(.)\\1+"); // 后面都使用第一個組
for (String str:string3) {
System.out.println("result: "+ str);
}
// result: 123
// result: 345
// result: 567
// result: 08
// result: 08
多個組
// (A)(B)
String text5 = "You cannot improve your past, but you can improve your future. Once time is wasted, life is wasted.";
String patternString5 = "(ou) (.+?) ";// 兩對括號后各有一個空格
Pattern pattern5 = Pattern.compile(patternString5);
Matcher matcher5 = pattern5.matcher(text5);
while(matcher5.find()) {
System.out.println("found: " + matcher5.group(1) + matcher5.group(2));
}
// found: oucannot
// found: oucan
組嵌套
A((B)C)(D)E正則式中有四組:組0是ABCDE,組1是BC募寨,組2是B族展;組3是D。(順序從最左邊括號開始計算,第0組默認是全部字符串)
String string4 = "ABCDE";
Pattern pat = Pattern.compile("A((B)C)(D)E");
Matcher mat = pat.matcher(string4);
while (mat.find()){
System.out.println("組0:" + mat.group(0)+" 組1:" + mat.group(1)+" 組2:" + mat.group(2)+" 組3:" + mat.group(3));
}
// 組0:ABCDE 組1:BC 組2:B 組3:D
6. Matcher.replaceAll()拔鹰、replaceFirst()
看方法名就知道replaceAll()用于全部替換匹配序列仪缸,replaceFirst() 只替換第一個匹配的。
String s1 = "Java is an object-oriented programming language格郁,Java is written by James Gosling first.";
String patternString11 = "((Java) (.+?)) ";
Pattern pattern6 = Pattern.compile(patternString11);
Matcher matcher6 = pattern6.matcher(s1);
String replaceAll = matcher6.replaceAll("C plus plus ");
System.out.println("所有 = " + replaceAll);
String replaceFirst = matcher6.replaceFirst("C plus plus ");
System.out.println("第一個 = " + replaceFirst);
// 所有 = C plus plus an object-oriented programming language腹殿,C plus plus written by James Gosling first.
// 第一個 = C plus plus an object-oriented programming language独悴,Java is written by James Gosling first.
屏蔽手機號中間四位 13840241234 --> 138****1234例书,注意組的位置。
String telNum = "13840241234";
String res5 = telNum.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1\\****$2");
System.out.println("換了么:"+res5);
// 換了么:138****1234
// 把2個重復中的一個刻炒,后面的用第一個組
String string5 = "123&&345%%456-----085555508";
String string6 = string5.replaceAll("(.)\\1","$1");
System.out.println("result: " + string6);
//result: 123&345%456---0855508
正則表達式在不同的程序語言中有著細微的差別决采。
差異.png
寫完嘍!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ
知識重在總結(jié)和梳理坟奥,只有不斷地去學習并運用树瞭,才能化為自己的東西。當你能為別人講明白的時候爱谁,說明自己已經(jīng)掌握了晒喷。
歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處访敌!
如果有錯誤的地方凉敲,或者有您的見解,還請不嗇賜教寺旺!
喜歡的話爷抓,麻煩點個贊!