正则表达式-基础
正則表達(dá)式-基礎(chǔ)
文章目錄
- 正則表達(dá)式-基礎(chǔ)
- 前言
- 初窺門(mén)徑
- 漸入佳境
- 神功小成
- 練功寶典
- 參考文章
前言
正則表達(dá)式,是一種特殊的字符串模式,用來(lái)匹配一系列符合某個(gè)規(guī)則的字符串。就好比用模具做產(chǎn)品,而正則就是這個(gè)模具,定義一種規(guī)則去匹配符合規(guī)則的字符串。
正則的意思是正規(guī)、規(guī)則。正則表達(dá)式的英文名是 Regular Expression,可以直譯為描述某種規(guī)則的表達(dá)式,一般縮寫(xiě)為 regex。正則表達(dá)式是從左往右匹配的。
初窺門(mén)徑
我們先給出一個(gè)很常見(jiàn)的例子,判斷一個(gè)字符串是否為一個(gè)有效的電話(huà)號(hào)碼。
正常的思路是先判斷字符串是否為11位,再判斷每一為是否為數(shù)字即可。
public static boolean isValidTelphone(String number) {//判斷長(zhǎng)度為11位if(number.length() != 11) {return false;}//判斷是否為數(shù)字for(int i=0;i<number.length();i++) {if(number.charAt(i)<'0' || number.charAt(i)>'9') {return false;}}return true; }使用正則表達(dá)式
public static boolean isValidTelphone(String number) {return number.matches("\\d{11}"); }這個(gè)例子告訴我們,使用正則表達(dá)式的代碼實(shí)現(xiàn)相當(dāng)簡(jiǎn)潔高效。當(dāng)然我們不急著講解這個(gè)例子的正則表達(dá)式是如何構(gòu)造的,繼續(xù)學(xué)習(xí)之后自然也就明白了。
我們先來(lái)講講正則表達(dá)式的精確匹配,一個(gè)普通的字符串,例如 abcabcabc,它如果用來(lái)做正則表達(dá)式匹配的話(huà),只能匹配它自己。也就是說(shuō)它只能匹配 abcabcabc,不能匹配 ababab 或者 AbcAbcAbc等其他任何字符串。
System.out.println("abc".matches("abc")); //輸出true System.out.println("abc".matches("ab")); //輸出false System.out.println("abc".matches("Abc")); //輸出false事實(shí)上正則表達(dá)式的精確匹配很少用到,但它作為一條基本規(guī)則應(yīng)該被了解。
對(duì)于精確匹配,實(shí)際上就是判斷字符串相等,我們使用 Java 語(yǔ)言的 String.equals()方法同樣也可以實(shí)現(xiàn)。
如果需要匹配的字符串含有特殊字符,就需要用 \ 進(jìn)行轉(zhuǎn)義。比如 a&b,進(jìn)行正則表達(dá)式匹配時(shí),需要使用 a\ &b,又因?yàn)?\ 也是特殊字符,它也需要轉(zhuǎn)義,所以 a\ &b 對(duì)應(yīng)的 Java 字符串是 a\ \ &b,它是用來(lái)匹配 a&b 的。
System.out.println("a&b".matches("a\\&b")); //輸出true請(qǐng)記住,這兩個(gè)反斜杠的意義并不一樣,一個(gè)是正則的轉(zhuǎn)義,一個(gè)是字符串的轉(zhuǎn)義。
回到我們最開(kāi)始的判斷電話(huà)號(hào)碼的例子,\ \d 的本意其實(shí)就是 \d。 \d 在正則表達(dá)式中表示匹配任意數(shù)字 ,d 是 digital 的意思。比如 00\d 就可以匹配 000、007、008 等。注意一下,\d 只能匹配單個(gè)數(shù)字。我們不可以使用 00\d 去匹配 0066 。
那么問(wèn)題來(lái)了,怎么才能匹配多個(gè)數(shù)字呢?
- 如果數(shù)字不多的話(huà),我們可以寫(xiě)多次 \d。比如 \d\d 能匹配兩個(gè)數(shù)字,\d\d\d 能匹配三個(gè)數(shù)字。
- 如果數(shù)字太多的話(huà),我們?cè)?\d 后面打上花括號(hào) { },{n}表示匹配 n 次 ,\d{10000} 就表示匹配10000個(gè)數(shù)字,
如果要匹配 n 至 m 次,用 {n,m} 即可。如果要至少匹配 n 次,用{n,} 。注意“,”之后沒(méi)有空格。
System.out.println("1".matches("\\d{1,2}")); //輸出true System.out.println("12".matches("\\d{1,2}")); //輸出true System.out.println("123".matches("\\d{1,2}")); //輸出false System.out.println("1234".matches("\\d{2,}")); //輸出true如果要至多匹配 m 次,用 {0,m} 即可 。千萬(wàn)不要想當(dāng)然地使用 {,m},只是因?yàn)檎裏o(wú)窮不好表示我們才用的 {n,},實(shí)際上根本沒(méi)有 {,m} 這樣的寫(xiě)法。
現(xiàn)在你應(yīng)該完全理解了判斷電話(huà)號(hào)碼的例子,讓我們繼續(xù)進(jìn)行學(xué)習(xí)。
漸入佳境
正則的基礎(chǔ)規(guī)則中,除了 \d,還有 \w 和 \s。
- w是 wordwordword 的縮寫(xiě),匹配一個(gè)常用字符,包括字母、數(shù)字、下劃線(xiàn)。
- s是 spacespacespace 的縮寫(xiě),匹配一個(gè)空白字符,包括空格鍵打出來(lái)的空格,以及 \t \n \r \f。
記住上面的三個(gè)規(guī)則之后,我們可以順帶記住幾個(gè)新規(guī)則。因?yàn)樵谡齽t表達(dá)式中,把字母換成大寫(xiě),就表示相反的意思。
- 用 \d 表示匹配一個(gè)數(shù)字,\D 則表示匹配一個(gè)非數(shù)字的字符
- 用 \w 表示匹配一個(gè)常用字符,\W 則表示匹配一個(gè)不是字母、數(shù)字、下劃線(xiàn)的字符
- 用 \s 表示匹配一個(gè)空白字符,\S 則表示匹配一個(gè)非空白字符
有時(shí)候,我們對(duì)某些位置的字符沒(méi)有要求,僅僅需要占個(gè)位置而已,我們可以使用 ... 字符,可以理解為 ... 能夠匹配任意字符。
System.out.println("a0b".matches("a.b")); //輸出true System.out.println("a_b".matches("a.b")); //輸出true System.out.println("a b".matches("a.b")); //輸出true同樣地,有時(shí)候我們對(duì)匹配次數(shù)也沒(méi)有要求,匹配任意次均可。這時(shí),我們使用 * 字符。 ?*? 是指可以匹配任意次,包括 0 次,也就是說(shuō),?*? 等價(jià)于 ${0,} 。
System.out.println("1234".matches("\\d*")); //輸出true System.out.println("1".matches("\\d*")); //輸出true System.out.println("".matches("\\d*")); //輸出true如果要判斷某個(gè)字符至少出現(xiàn)一次,那就可以使用 +++ 字符進(jìn)行匹配, +++ 表示至少匹配一次,等價(jià)于 1,{1,}1, 。
System.out.println("1234".matches("\\d+")); //輸出true System.out.println("1".matches("\\d+")); //輸出true System.out.println("".matches("\\d+")); //輸出false還有一種場(chǎng)景,如果某個(gè)字符要么匹配 0 次,要么匹配 1 次,我們就可以用 ??? 匹配,它等價(jià)于 0,1{0,1}0,1 。
System.out.println("".matches("\\d?")); //輸出true System.out.println("1".matches("\\d?")); //輸出true System.out.println("1234".matches("\\d?")); //輸出false請(qǐng)一定要記住這些規(guī)則哦。
- ... 表示匹配任意字符
- ?*? 匹配任意次,包括 0 次,?*? 等價(jià)于 0,{0,}0,
- +++ 匹配至少 1 次,它等價(jià)于 1,{1,}1,
- ??? 匹配 0 次或 1 次
神功小成
學(xué)習(xí)到這里,你已經(jīng)掌握了不少正則表達(dá)式規(guī)則了,但是不能驕傲自滿(mǎn),我們還要把一些容易埋坑的地方給踩掉。
回到我們最開(kāi)始的判斷電話(huà)號(hào)碼的例子,如果我們規(guī)定電話(huà)號(hào)碼不能以 0 開(kāi)頭,那么應(yīng)該怎么寫(xiě)正則表達(dá)式呢?
很明顯,如果用 d11\\d{11}d11 來(lái)匹配的話(huà),0 是會(huì)進(jìn)行匹配的,不滿(mǎn)足我們的要求。這樣的場(chǎng)景我們需要用 [ ] 來(lái)匹配, [ ] 用于匹配指定范圍內(nèi)的字符,比如 [123456789][123456789][123456789] 可以匹配 1-9。
聰明如你,看到這里應(yīng)該就明白了,解決這個(gè)問(wèn)題的正則表達(dá)式是
[123456789]\\d{10}這里有一個(gè)小技巧,[123456789][123456789][123456789] 寫(xiě)起來(lái)太麻煩,可以寫(xiě)作[1?9][1-9][1?9]。類(lèi)似地,[a?g][a-g][a?g] 表示 [abcdefg][abcdefg][abcdefg],[U?Z][U-Z][U?Z] 表示 [UVWXYZ][UVWXYZ][UVWXYZ]。
如果既可以是數(shù)字 1-9,又可以是字母 a-g,還可以是字母 U-Z,我們可以這么寫(xiě) [1?9a?gU?Z][1-9a-gU-Z][1?9a?gU?Z]。
System.out.println("3".matches("[1-9a-gU-Z]")); //輸出true System.out.println("d".matches("[1-9a-gU-Z]")); //輸出true System.out.println("Y".matches("[1-9a-gU-Z]")); //輸出true System.out.println("A".matches("[1-9a-gU-Z]")); //輸出false如果是 0-1,8-9 這樣的組合,我們可以直接寫(xiě)為 [0189][0189][0189],這樣非常簡(jiǎn)潔。我們也可以按照規(guī)則把它寫(xiě)為 [0?18?9][0-18-9][0?18?9],因?yàn)檎齽t一次只匹配一個(gè)字符,所以這樣寫(xiě)不會(huì)產(chǎn)生歧義,計(jì)算機(jī)不會(huì)把它誤解成要匹配 0-18 之類(lèi)的。
System.out.println("9".matches("[0189]")); //輸出true System.out.println("8".matches("[0-18-9]")); //輸出true System.out.println("5".matches("[0-18-9]")); //輸出false還有一種寫(xiě)法可以實(shí)現(xiàn)這一點(diǎn),那就是“或”運(yùn)算符。正則的“或”運(yùn)算符是 ∣|∣,[0189][0189][0189] 也可以寫(xiě)成 [0∣1∣8∣9][0|1|8|9][0∣1∣8∣9]。
System.out.println("1".matches("[0|1|8|9]")); //輸出true System.out.println("5".matches("[0|1|8|9]")); //輸出false“或”可以實(shí)現(xiàn)很多功能,它不局限于單個(gè)字符。關(guān)于“或”的妙用,多多上機(jī)練習(xí)慢慢就會(huì)發(fā)現(xiàn)了。
System.out.println("ab".matches("ab|AB")); //輸出true System.out.println("AB".matches("ab|AB")); //輸出true System.out.println("12".matches("ab|AB")); //輸出false如果我們想排除某些字符,比如這個(gè)位置不能是 [123][123][123]。之前說(shuō)的取反是針對(duì)字母的,[][][]沒(méi)有大寫(xiě)這一說(shuō),[][][]取反的方式是 [^],比如不能是 [123][123][123] 的表示方法是 [^123] 或者 [^1-3]。
System.out.println("5".matches("[^1-3]")); //輸出true System.out.println("2".matches("[^1-3]")); //輸出false我們?cè)俳榻B一個(gè)貪婪模式與非貪婪模式。貪婪模式在基礎(chǔ)部分僅做了解,在進(jìn)階部分我們會(huì)用例子詳細(xì)說(shuō)明。
- 貪婪就是盡量往多的匹配,比如"+",匹配一次或多次 ,優(yōu)先匹配更多的。
- 非貪婪模式就是盡量少的匹配,比如"?",匹配0次或1次,優(yōu)先匹配少的。
- 編程中如何區(qū)分呢,在貪婪匹配后面加個(gè)"?",就變成了非貪婪模式。
學(xué)習(xí)到了這一步,可以恭喜你完成了對(duì)新手的全部教程。你現(xiàn)在所學(xué)習(xí)的正則知識(shí)已經(jīng)足夠你應(yīng)付許多應(yīng)用場(chǎng)景了。
現(xiàn)在你可以多看看練功寶典,投入到實(shí)踐的海洋中,多多練習(xí),扎實(shí)基礎(chǔ)。也可以選擇繼續(xù)深入地進(jìn)行學(xué)習(xí),掌握更高深的正則規(guī)則。正則表達(dá)式-進(jìn)階
練功寶典
常用的匹配規(guī)則
| \d | 匹配任意數(shù)字,等價(jià)于[0-9] |
| \D | 匹配任意非數(shù)字的字符 |
| \w | 匹配字母、數(shù)字及下劃線(xiàn) |
| \W | 匹配不是字母、數(shù)字及下劃線(xiàn)的字符 |
| \s | 匹配任意空白字符,等價(jià)于[\t\n\r\f] |
| \S | 匹配任意非空字符 |
| \n | 匹配一個(gè)換行符 |
| \t | 匹配一個(gè)制表符 |
| ^ | 匹配一行字符串的開(kāi)頭 |
| $ | 匹配一行字符串的結(jié)尾 |
| . | 匹配任意字符,不特殊指定話(huà)默認(rèn)不匹配換行符 |
| […] | 用來(lái)表示一組字符,比如[ack]匹配a、c、k |
| [^…] | 不在[…]中的字符,比如[^abc]匹配除了a、b、c之外的字符 |
| * | 匹配0個(gè)或多個(gè)表達(dá)式 |
| + | 匹配1個(gè)或多個(gè)表達(dá)式 |
| ? | 匹配0個(gè)或1個(gè)前面的正則表達(dá)式定義的片段,非貪婪方式 |
| {n} | 精確匹配n個(gè)前面的表達(dá)式 |
| {n,m} | 匹配n到m次前面正則表達(dá)式定義的片段,貪婪方式 |
| a|b | 匹配a或b |
| ( ) | 匹配括號(hào)內(nèi)的表達(dá)式,也表示一個(gè)組 |
參考文章
LeetCode力扣——【正則表達(dá)式】王國(guó)奇遇記
總結(jié)
- 上一篇: 练习2: Python基本图形绘制 (第
- 下一篇: 正则表达式-进阶