Java正則表達式詳解

正則表達式乍一看上去是一堆繁瑣的解孙、無規(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)載請注明出處访敌!

本文地址:http://www.reibang.com/p/6cb7e37e9e70

如果有錯誤的地方凉敲,或者有您的見解,還請不嗇賜教寺旺!

喜歡的話爷抓,麻煩點個贊!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阻塑,一起剝皮案震驚了整個濱河市蓝撇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陈莽,老刑警劉巖渤昌,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虽抄,死亡現(xiàn)場離奇詭異,居然都是意外死亡独柑,警方通過查閱死者的電腦和手機极颓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來群嗤,“玉大人菠隆,你說我怎么就攤上這事】衩兀” “怎么了骇径?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長者春。 經(jīng)常有香客問我破衔,道長,這世上最難降的妖魔是什么钱烟? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任晰筛,我火速辦了婚禮,結(jié)果婚禮上拴袭,老公的妹妹穿的比我還像新娘读第。我一直安慰自己,他們只是感情好拥刻,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布怜瞒。 她就那樣靜靜地躺著,像睡著了一般般哼。 火紅的嫁衣襯著肌膚如雪吴汪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天蒸眠,我揣著相機與錄音漾橙,去河邊找鬼。 笑死楞卡,一個胖子當著我的面吹牛霜运,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臀晃,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼觉渴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了徽惋?” 一聲冷哼從身側(cè)響起案淋,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎险绘,沒想到半個月后踢京,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體誉碴,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年瓣距,在試婚紗的時候發(fā)現(xiàn)自己被綠了黔帕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹈丸,死狀恐怖成黄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逻杖,我是刑警寧澤奋岁,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站荸百,受9級特大地震影響闻伶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜够话,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一蓝翰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧女嘲,春花似錦畜份、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至媒至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谷徙,已是汗流浹背拒啰。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留完慧,地道東北人谋旦。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像屈尼,于是被迫代替她去往敵國和親册着。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容