java正则匹配英文句号_「正则表达式」王国奇遇记
第一回 初來(lái)乍到
NPC: "歡迎來(lái)到正則表達(dá)式的國(guó)度,勇士!這里的每一個(gè)人都使用正則表達(dá)式,我是這里的 NPC,每一個(gè)來(lái)到這里的人都將由我代為介紹正則世界的規(guī)則,至于能領(lǐng)悟到何種境界,就看你的造化了。祝你好運(yùn),勇士!"
你:"啊,好的,正則表達(dá)式......有點(diǎn)奇怪的名字,它是什么呢?"
NPC:"什么?你還沒(méi)有聽(tīng)過(guò)正則表達(dá)式,真是一個(gè)莽撞的小伙子。看來(lái)你也和外面世界的人一樣,每次只有用到字符串匹配 時(shí),才會(huì)通過(guò)「谷鴿」來(lái)我們的國(guó)度尋找答案。一群知其然不知其所以然的家伙。"
說(shuō)著,NPC 身前浮現(xiàn)出幾個(gè)鎏金大字:
正則表達(dá)式:用來(lái)匹配一系列符合某個(gè)規(guī)則的字符串的表達(dá)式。"正則的意思是正規(guī)、規(guī)則。正則表達(dá)式的英文名是 Regular Expression,可以直譯為描述某種規(guī)則的表達(dá)式,一般縮寫(xiě)為 regex" ,NPC 緩緩說(shuō)道。
第二回 牛刀小試
NPC:"我先來(lái)考考你吧:你如何判斷一個(gè)字符串是不是有效的電話號(hào)碼?這可是一個(gè)非常常見(jiàn)的需求。"
你:"沒(méi)問(wèn)題,我以前確實(shí)寫(xiě)過(guò)一份類(lèi)似的代碼。首先判斷字符串是否是 11 位,再判斷每一位是否都是數(shù)字就可以了。"
public static boolean isValidPhoneNumber(String number) {// 判斷是否是 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; }NPC:"好了好了,快把你這份代碼藏好,這份代碼放到我們正則的國(guó)度是會(huì)被笑掉大牙的。看看我們國(guó)度的人是怎么實(shí)現(xiàn)這份需求的吧!"
public static boolean isValidPhoneNumber(String number) {return number.matches("d{11}"); }你:"啊?如此簡(jiǎn)潔的實(shí)現(xiàn),正則強(qiáng)者竟恐怖如斯!"
NPC:"這可不是什么強(qiáng)者寫(xiě)的代碼,充其量算是牛刀小試罷了。"
第三回 初窺門(mén)徑
NPC:"我先給你講講正則表達(dá)式的精確匹配。一個(gè)普通的字符串,比如 abc,它如果用來(lái)做正則表達(dá)式匹配的話,只能匹配自己。也就是說(shuō)它只能匹配字符串 abc,不能匹配 ab,Abc 等其他任何字符串。"
System.out.println("abc".matches("abc")); // 輸出為 true System.out.println("ab".matches("abc")); // 輸出為 false System.out.println("Abc".matches("abc")); // 輸出為 false你:"這好像沒(méi)什么用,需要精確匹配的話,我們可以用String.equals()函數(shù),不需要用正則吧?"
NPC:"沒(méi)錯(cuò),正則表達(dá)式的精確匹配很少用到。我只是在給你介紹正則表達(dá)式的一條基本規(guī)則而已。"
NPC:"如果需要匹配的字符串含有特殊字符,那就需要用 轉(zhuǎn)義。比如 a&b,在用正則表達(dá)式匹配時(shí),需要使用 a&b,又由于在 Java 字符串中, 也是特殊字符,它也需要轉(zhuǎn)義,所以 a&b 對(duì)應(yīng)的 Java 字符串是 a&b,它是用來(lái)匹配 a&b 的。"
System.out.println("a&b".matches("a&b")); // 輸出為 true你:"這么說(shuō)來(lái),這兩個(gè)反斜杠的意義竟然還不一樣:一個(gè)是正則的轉(zhuǎn)義,一個(gè)是 Java 字符串的轉(zhuǎn)義。那么我們之前那個(gè)匹配電話號(hào)碼的例子里面,d的本意也是d嗎?"
NPC:"不錯(cuò)不錯(cuò),算你還有點(diǎn)悟性。d在正則表達(dá)式中表示匹配任意數(shù)字,d 是 digital 的簡(jiǎn)寫(xiě)。比如00d就可以匹配000,007,008等等。"
你:"那么,00d可以匹配0066嗎?"
NPC:"不能,d只能匹配單個(gè)數(shù)字。"
你:"那我要怎么才能匹配多個(gè)數(shù)字呢?"
NPC:"你可以寫(xiě)多次,比如dd就能匹配兩個(gè)數(shù)字,ddd能匹配三個(gè)數(shù)字,需要匹配幾個(gè)數(shù)字就寫(xiě)幾次就行了。"
System.out.println("1".matches("dd")); // 輸出為 false System.out.println("11".matches("dd")); // 輸出為 true System.out.println("111".matches("dd")); // 輸出為 false你:"那我如果要匹配 10000 個(gè)數(shù)字呢?總不能寫(xiě)一萬(wàn)次吧?"
NPC:"那就像我們剛才匹配電話號(hào)碼的例子一樣,在 d 后面打上花括號(hào) {},{n} 表示匹配 n 次。d{10000} 就表示匹配 10000 個(gè)數(shù)字。"
你:"原來(lái)如此,現(xiàn)在我能完全看懂剛才寫(xiě)的匹配電話號(hào)碼的例子了!"
NPC:"趁熱打鐵,如果要匹配 n ~ m 次,用 {n,m} 即可,如果要匹配至少 n 次,用 {n,} 即可。需要注意 , 后不能有空格。"
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}")); // 輸出為 falseSystem.out.println("123".matches("d{2,}")); // 輸出為 true"按照這個(gè)寫(xiě)法,如果要匹配最多m次,是不是用{,m}? "你若有所思。
NPC:"剛夸了你有點(diǎn)悟性又被你蠢哭了,最多 m 次需要這么寫(xiě)嗎?直接用{0,m}不就行了嗎?只是因?yàn)檎裏o(wú)窮不好表示我們才用的{n,},在正則國(guó)度根本沒(méi)有{,m}這樣的寫(xiě)法。 "
你:"啊,原來(lái)如此,我想多了。"
第四回 小有所成
NPC:"正則的基礎(chǔ)規(guī)則中,除了d,還有w和s,w 是 word 的簡(jiǎn)寫(xiě),表示匹配一個(gè)常用字符,包括字母、數(shù)字、下劃線。s 是 space 的簡(jiǎn)寫(xiě),表示匹配一個(gè)空格,包括三種:
- 空格鍵打出來(lái)的空格
- Tab 鍵打出來(lái)的空格
- 回車(chē)鍵打出來(lái)的空格"
你:"Tab 鍵打出來(lái)的空格和回車(chē)鍵打出來(lái)的空格?是指t和n嗎?"
NPC:"完全正確。"
你:"我明白了,我來(lái)測(cè)試一下。"
System.out.println("LeetCode_666".matches("w{12}")); // 輸出為 true System.out.println("t n".matches("s{3}")); // 輸出為 true System.out.println("LeettCode 666".matches("w{4}sw{4}sd{3}")); // 輸出為 trueNPC:"非常棒,我的勇士!希望這三個(gè)基本規(guī)則還不至于讓你記昏了頭。不過(guò)請(qǐng)放心,沒(méi)有其他字母需要記憶了,只有這三個(gè)而已。"
第五回 更進(jìn)一步
NPC:"記住上面三個(gè)規(guī)則之后,你還可以順帶獲得幾個(gè)新的規(guī)則。因?yàn)檎齽t國(guó)度規(guī)定:將字母換成大寫(xiě),就表示相反的意思。用 d 你可以匹配一個(gè)數(shù)字,D 則表示匹配一個(gè)非數(shù)字。"
System.out.println("a".matches("d")); // 輸出為 false System.out.println("1".matches("d")); // 輸出為 trueSystem.out.println("a".matches("D")); // 輸出為 true System.out.println("1".matches("D")); // 輸出為 false你:"哈,設(shè)計(jì)者真是太機(jī)智了,大大減少了我這種新手的學(xué)習(xí)成本。"
NPC:"是的,這非常好記。類(lèi)似地,W 可以匹配 w 不能匹配的字符,S 可以匹配 s 不能匹配的字符。"
第六回 漸入佳境
NPC:"有時(shí)候,我們對(duì)某些位置的字符沒(méi)有要求,僅需要占個(gè)位置即可。這時(shí)候我們就可以用 . 字符。"
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你:"那是不是也可以理解為:.可以匹配任意字符。"
NPC:"是的,可以這么理解。還記得之前說(shuō)的{n}表示匹配n次嗎?有時(shí)候,我們對(duì)匹配的次數(shù)沒(méi)有要求,匹配任意次均可,這時(shí),我們就可以用*字符。"
System.out.println("1".matches("d*")); // 輸出為 true System.out.println("123".matches("d*")); // 輸出為 true System.out.println("".matches("d*")); // 輸出為 true你:"我有疑問(wèn),為什么第三個(gè)表達(dá)式也會(huì)輸出 true 呢?明明沒(méi)有出現(xiàn)數(shù)字啊?"
NPC:"那意味著出現(xiàn)了 0 次,* 是指 可以匹配任意次,包括 0 次。也就是說(shuō),* 等價(jià)于 {0,}"
你:"我感覺(jué)比較常見(jiàn)的需求應(yīng)該是某個(gè)字符至少出現(xiàn)一次吧?"
NPC:"那就可以用 + 匹配,+ 表示 至少匹配一次。它等價(jià)于 {1,}"
System.out.println("1".matches("d+")); // 輸出為 true System.out.println("123".matches("d+")); // 輸出為 true System.out.println("".matches("d+")); // 輸出為 false你:"哈哈,看來(lái)設(shè)計(jì)者也發(fā)現(xiàn)了這個(gè)需求更常用。平時(shí)+號(hào)比*號(hào)用得多吧"!你感覺(jué)自己猜到了語(yǔ)法設(shè)計(jì)者的想法,洋洋得意地對(duì) NPC 說(shuō)道。
"這倒沒(méi)人統(tǒng)計(jì)過(guò)",NPC 白了你一眼,"在我們正則的國(guó)度,常常是一個(gè)場(chǎng)景一個(gè)正則,不存在誰(shuí)比誰(shuí)更常用的對(duì)比,按照實(shí)際場(chǎng)景使用就行了。"
NPC:"還有一種場(chǎng)景,如果某個(gè)字符要么匹配 0 次,要么匹配 1 次,我們就可以用?匹配。它等價(jià)于{0,1}"
System.out.println("".matches("d?")); // 輸出為 true System.out.println("1".matches("d?")); // 輸出為 true System.out.println("123".matches("d?")); // 輸出為 false你:".匹配任意字符;*匹配任意次,包括 0 次;+號(hào)匹配至少 1 次,?匹配 0 次或 1 次。我記住了!"
第七回 心浮氣躁
一下子掌握了這么多的正則匹配規(guī)則的你有點(diǎn)飄飄然,于是你對(duì) NPC 說(shuō)道:"我感覺(jué)我已經(jīng)掌握了夠多的匹配規(guī)則,足以應(yīng)付所有的字符串匹配場(chǎng)景了!"
NPC:"是的,你已經(jīng)掌握了足夠多的規(guī)則,勇士。可先別得意得太早,我再考考你吧。看看匹配電話號(hào)碼的程序,如果我們規(guī)定電話號(hào)碼不能以 0 開(kāi)頭,應(yīng)該怎么寫(xiě)正則表達(dá)式呢?"
"不能以0開(kāi)頭,那就不能用d{11}了,這......",你抓耳撓腮,為難起來(lái)。
這時(shí),調(diào)皮的 NPC 學(xué)著你剛才的樣子,說(shuō)道:"我已經(jīng)掌握了足夠多的匹配規(guī)則,足以應(yīng)付所有的字符串匹配場(chǎng)景了!"
你:"呃,還差一點(diǎn)......快別取笑我了,快告訴我這個(gè)要用什么新的規(guī)則吧!"
"年輕人啊,總是心浮氣躁",NPC 搖了搖頭,"這樣的場(chǎng)景需要用[]來(lái)匹配,[]用于匹配指定范圍內(nèi)的字符,比如[123456789]可以匹配 1~9。"
System.out.println("1".matches("[1-9a-gU-Z]")); // 輸出為 true System.out.println("b".matches("[1-9a-gU-Z]")); // 輸出為 true System.out.println("X".matches("[1-9a-gU-Z]")); // 輸出為 true System.out.println("A".matches("[1-9a-gU-Z]")); // 輸出為 false你:"這可真是太方便了!如果是 0~1,8~9 可以這樣組合嗎?"
NPC:"那樣的話,你寫(xiě) [0189] 不是更簡(jiǎn)潔嗎?"
你:"我想學(xué)習(xí)(裝 X)。"
NPC:"那當(dāng)然也是可以的,[0-18-9] 正是你想要的。由于正則一次只匹配一個(gè)字符,所以這樣寫(xiě)并不會(huì)有歧義,也就是說(shuō)計(jì)算機(jī)不會(huì)把這種寫(xiě)法誤解成要匹配 0~18 之類(lèi)的。"
System.out.println("1".matches("[0-18-9]")); // 輸出為 true System.out.println("5".matches("[0-18-9]")); // 輸出為 falseNPC:"還有一種寫(xiě)法可以實(shí)現(xiàn)這一點(diǎn),那就是用或運(yùn)算符,正則的或運(yùn)算符是|,[0189]也可以寫(xiě)作0|1|8|9。"
System.out.println("1".matches("0|1|8|9")); // 輸出為 true System.out.println("5".matches("0|1|8|9")); // 輸出為 false你:"所以說(shuō)范圍就是或的簡(jiǎn)寫(xiě),對(duì)嗎?"
NPC:"不對(duì),或可以實(shí)現(xiàn)更多的功能,它并不局限于單個(gè)字符。"
System.out.println("abc".matches("abc|ABC")); // 輸出為 true System.out.println("ABC".matches("abc|ABC")); // 輸出為 true System.out.println("123".matches("abc|ABC")); // 輸出為 false你:"如果我想排除某些字符呢?比如這個(gè)位置不能是[123]。我記得你之前說(shuō)正則王國(guó)以大寫(xiě)表示取反,[]要怎么大寫(xiě)呢?"
NPC:"[]可沒(méi)有大寫(xiě)之說(shuō),[]取反的方式是:[^],比如不能是[123]的表示方法為[^123]或者[^1-3]"
你:"原來(lái)如此,我懂了。現(xiàn)在還有什么規(guī)則我沒(méi)有學(xué)到的嗎?"
NPC:"新手教程到這里就結(jié)束了,這已經(jīng)足夠你應(yīng)付許多應(yīng)用場(chǎng)景了。但我這還有兩本高手秘籍,你想不想學(xué)呢?"
你:"高手秘籍!聽(tīng)著都讓人激動(dòng)啊,快講講!"
第八回 探囊取物
NPC:"這第一本秘籍的名字叫 探囊取物。考慮一個(gè)實(shí)際需求,有許許多多以下格式的字符串,你需要用正則表達(dá)式匹配出其姓名和年齡。
- Name:Aurora Age:18
- 其中還夾雜著一些無(wú)關(guān)緊要的數(shù)據(jù)
- Name:Bob Age:20
- 錯(cuò)誤的數(shù)據(jù)有著各種各樣錯(cuò)誤的格式
- Name:Cassin Age:22
- ..."
你:"沒(méi)問(wèn)題,這已經(jīng)難不倒我了。讓我想想......觀察字符串的規(guī)則,只需要用 Name:w+s*Age:d{1,3} 就能匹配了。"
System.out.println("Name:Aurora Age:18".matches("Name:w+s*Age:d{1,3}")); // 輸出為 true System.out.println("其中還夾雜著一些無(wú)關(guān)緊要的數(shù)據(jù)".matches("Name:w+s*Age:d{1,3}")); // 輸出為 false System.out.println("Name:Bob Age:20".matches("Name:w+s*Age:d{1,3}")); // 輸出為 true System.out.println("錯(cuò)誤的數(shù)據(jù)有著各種各樣錯(cuò)誤的格式".matches("Name:w+s*Age:d{1,3}")); // 輸出為 false System.out.println("Name:Cassin Age:22".matches("Name:w+s*Age:d{1,3}")); // 輸出為 trueNPC:"很好!一般來(lái)說(shuō),下一步你要做的就是取出這些表達(dá)式中的姓名和年齡,以便把它們存到數(shù)據(jù)庫(kù)中。"
你:"那我可以用 indexOf 和 subString 函數(shù)來(lái)取這些值。 "
NPC:"的確可行,但你現(xiàn)在不需要那個(gè)蠢辦法了,我的勇士。你已經(jīng)掌握了正則的力量,在我們正則國(guó)度有更簡(jiǎn)潔的取值方式。"
Pattern pattern = Pattern.compile("Name:(w+)s*Age:(d{1,3})"); Matcher matcher = pattern.matcher("Name:Aurora Age:18"); if(matcher.matches()) {String group1 = matcher.group(1);String group2 = matcher.group(2);System.out.println(group1); // 輸出為 AuroraSystem.out.println(group2); // 輸出為 18 }NPC:"看吧,只要用()將需要取值的地方括起來(lái),傳給 Pattern 對(duì)象,再用 Pattern 對(duì)象匹配后獲得的 Matcher 對(duì)象來(lái)取值就行了。每個(gè)匹配的值將會(huì)按照順序保存在 Matcher 對(duì)象的 group 中。"
NPC:"你可以看到我用()把w+和d{1,3}分別括起來(lái)了,判斷 Pattern 對(duì)象與字符串是否匹配的方法是Matcher.matches(),如果匹配成功,這個(gè)函數(shù)將返回 true,如果匹配失敗,則返回 false。"
你:"這里是不是寫(xiě)錯(cuò)了,為什么 group 是從下標(biāo) 1 開(kāi)始取值的,計(jì)算機(jī)不都從 0 開(kāi)始數(shù)嗎?"
NPC:"并沒(méi)有寫(xiě)錯(cuò),這是因?yàn)?group(0) 被用來(lái)保存整個(gè)匹配的字符串了。"
System.out.println(matcher.group(0)); // 輸出為 Name:Aurora Age:18你:"原來(lái)是這樣,分組可真是太方便了。但我們之前都是用的String.matches方法來(lái)匹配的正則表達(dá)式,這里用的 Pattern 又是什么呢?"
NPC:"想知道這個(gè)問(wèn)題的答案的話,我們不妨來(lái)看一下String.matches方法的源碼。"
public boolean matches(String regex) {return Pattern.matches(regex, this); }"源碼中調(diào)用了Pattern.matches方法,我們?cè)俑M(jìn)去。"
public static boolean matches(String regex, CharSequence input) {Pattern p = Pattern.compile(regex);Matcher m = p.matcher(input);return m.matches(); }你:"啊,我明白了!原來(lái) Pattern 并不是什么新鮮東西,String.matches內(nèi)部就是調(diào)用的 Pattern,兩種寫(xiě)法的原理是一模一樣的!"
NPC:"沒(méi)錯(cuò),并且閱讀源碼之后,你可以發(fā)現(xiàn),每次調(diào)用String.matches函數(shù),都會(huì)新建出一個(gè) Pattern 對(duì)象。所以如果要用同一個(gè)正則表達(dá)式多次匹配字符串的話,最佳的做法不是直接調(diào)用String.matches方法,而應(yīng)該先用正則表達(dá)式新建一個(gè) Pattern 對(duì)象,然后反復(fù)使用,以提高程序運(yùn)行效率。"
// 錯(cuò)誤的做法,每次都會(huì)新建一個(gè) Pattern,效率低 boolean result1 = "Name:Aurora Age:18".matches("Name:(w+)s*Age: (d{1,3})"); boolean result2 = "Name:Bob Age:20".matches("Name:(w+)s*Age: (d{1,3})"); boolean result3 = "Name:Cassin Age:22".matches("Name:(w+)s*Age: (d{1,3})");// 正確的做法,復(fù)用同一個(gè) Pattern,效率高 Pattern pattern = Pattern.compile("Name:(w+)s*Age:(d{1,3})"); boolean result4 = pattern.matcher("Name:Aurora Age:18").matches(); boolean result5 = pattern.matcher("Name:Bob Age:20").matches(); boolean result6 = pattern.matcher("Name:Cassin Age:22").matches();第九回 移花接木
NPC:"我這第二本秘籍名為 移花接木。再考慮一個(gè)實(shí)際場(chǎng)景:你有一個(gè)讓用戶(hù)輸入標(biāo)簽的輸入框,用戶(hù)可以輸入多個(gè)標(biāo)簽。可是你并沒(méi)有提示用戶(hù),標(biāo)簽之前用什么間隔符號(hào)隔開(kāi)。"
你:"你還別說(shuō),我之前真遇到過(guò)這個(gè)問(wèn)題。結(jié)果用戶(hù)的輸入五花八門(mén),有用逗號(hào)的,有用分號(hào)的,有用空格的,還有用制表符的......"
- 二分,回溯,遞歸,分治
- 搜索;查找;旋轉(zhuǎn);遍歷
- 數(shù)論 圖論 邏輯 概率
NPC:"那你是怎么解決的呢?"
你:"用 String.split 函數(shù)唄,這個(gè)函數(shù)我已經(jīng)用得很熟練了。將各種分隔符號(hào)依次傳入嘗試,最后總算是解決了。"
public static String[] splitTabs(String tabs) {if(tabs.split(",").length == 4) return tabs.split(",");if(tabs.split(";").length == 4) return tabs.split(";");if(tabs.split(" ").length == 4) return tabs.split(" ");return new String[0]; }public static void main(final String[] args){System.out.println(Arrays.toString(splitTabs("二分,回溯,遞歸,分治")));System.out.println(Arrays.toString(splitTabs("搜索;查找;旋轉(zhuǎn);遍歷")));System.out.println(Arrays.toString(splitTabs("數(shù)論 圖論 邏輯 概率"))); }輸出為:
[二分, 回溯, 遞歸, 分治] [搜索, 查找, 旋轉(zhuǎn), 遍歷] [數(shù)論, 圖論, 邏輯, 概率]這時(shí),你看到 NPC 露出了心痛的表情:"暴殄天物啊!你這種行為就好比拿著精心打磨的鉆石當(dāng)電鉆頭,這樣的代碼在我們正則王國(guó)是會(huì)遭人唾罵的。"
你:"String.split 函數(shù)不就是用來(lái)分割字符串的嗎?"
NPC:"當(dāng)然是,但 split 函數(shù)可不是你這樣用的,不知你是否看過(guò) split 函數(shù)的源碼,這個(gè)函數(shù)傳入的參數(shù)實(shí)際上是一個(gè)正則表達(dá)式。"
你:"啊?但我之前沒(méi)寫(xiě)過(guò)正則表達(dá)式,分割出來(lái)也沒(méi)出錯(cuò)啊!"
NPC:"當(dāng)然,你忘了我最開(kāi)始給你講的了嗎?你直接使用字符串,在正則王國(guó)屬于精確匹配,只能匹配你寫(xiě)死的那個(gè)字符串。"
你:"原來(lái)如此。那么我應(yīng)該怎么做呢?"
NPC:"當(dāng)然是用正則表達(dá)式模糊匹配,只要能匹配成功,就以其分割。"
System.out.println(Arrays.toString("二分,回溯,遞歸,分治".split("[,;s] +"))); System.out.println(Arrays.toString("搜索;查找;旋轉(zhuǎn);遍歷".split("[,;s] +"))); System.out.println(Arrays.toString("數(shù)論 圖論 邏輯 概率".split("[,;s] +")));輸出為:
[二分, 回溯, 遞歸, 分治] [搜索, 查找, 旋轉(zhuǎn), 遍歷] [數(shù)論, 圖論, 邏輯, 概率]你:"原來(lái) split 函數(shù)這么強(qiáng)大,我以后不會(huì)犯這種錯(cuò)誤了!"
NPC:"字符串中,可不止這一個(gè)函數(shù)是傳入的正則表達(dá)式,你還記得替換所有匹配字符串用的什么函數(shù)嗎?"
你:"用的是 replaceAll 函數(shù),這個(gè)函數(shù)不會(huì)也是傳的正則表達(dá)式吧!"
NPC:"正是這樣,所以我們可以用正則表達(dá)式模糊匹配,將符合規(guī)則的字符串全部替換掉。比如就現(xiàn)在這個(gè)例子,我們可以把用戶(hù)輸入的所有數(shù)據(jù)統(tǒng)一規(guī)范為使用 ; 分隔,那我們就可以這樣寫(xiě)。"
System.out.println("二分,回溯,遞歸,分治".replaceAll("[,;s]+", ";")); System.out.println("搜索;查找;旋轉(zhuǎn);遍歷".replaceAll("[,;s]+", ";")); System.out.println("數(shù)論 圖論 邏輯 概率".replaceAll("[,;s]+", ";"));輸出為:
二分;回溯;遞歸;分治 搜索;查找;旋轉(zhuǎn);遍歷 數(shù)論;圖論;邏輯;概率你:"果然是移花接木,模糊匹配比精確匹配效率高多了!"
NPC:"還不止這一點(diǎn),在 replaceAll 的第二個(gè)參數(shù)中,我們可以通過(guò)$1,$2,...來(lái)反向引用匹配到的子串。只要將需要引用的部分用()括起來(lái)就可以了。"
System.out.println("二分,回溯,遞歸,分治".replaceAll("([,;s]+)", "---$1---")); System.out.println("搜索;查找;旋轉(zhuǎn);遍歷".replaceAll("([,;s]+)", "---$1---")); System.out.println("數(shù)論 圖論 邏輯 概率".replaceAll("([,;s]+)", "---$1---"));輸出為:
二分---,---回溯---,---遞歸---,---分治 搜索---;---查找---;---旋轉(zhuǎn)---;---遍歷 數(shù)論--- ---圖論--- ---邏輯--- ---概率你:"哈,有時(shí)候我們不需要替換,只需要將正則匹配出來(lái)的部分添加一些前綴或后綴,就可以用這種方式!"
NPC:"完全正確。"
第十回 驀然回首
NPC:"恭喜你學(xué)完了所有的正則教程,現(xiàn)在你知道正則表達(dá)式是什么了吧。"
你:"沒(méi)錯(cuò),以前總感覺(jué)正則表達(dá)式晦澀難懂,每次用到時(shí)就去網(wǎng)上搜索答案,現(xiàn)在看來(lái)也不過(guò)如此。"
NPC:"說(shuō) 不過(guò)如此 倒是有些托大了,雖然我給你介紹了正則表達(dá)式的基本規(guī)則,但正則表達(dá)式里面還有不少的學(xué)問(wèn)可以去挖掘的。每種技術(shù)都有一個(gè)熟能生巧的過(guò)程。"
你:"什么?還有學(xué)問(wèn)?我感覺(jué)我已經(jīng)學(xué)完了啊!還有什么學(xué)問(wèn),一并給我講了吧!"
NPC:"那你看這樣一道題:給你一些字符串,統(tǒng)計(jì)其末尾 e 的個(gè)數(shù):
- LeetCode
- LeetCodeeee
- LeetCodeee"
你:"看起來(lái)并不難,用(w+)(e*)匹配,再取 group(2) 判斷即可。"
Pattern pattern = Pattern.compile("(w+)(e*)"); Matcher matcher = pattern.matcher("LeetCode"); if (matcher.matches()) {String group1 = matcher.group(1);String group2 = matcher.group(2);System.out.println("group1 = " + group1 + ", length = " + group1.length());System.out.println("group2 = " + group2 + ", length = " + group2.length()); }NPC:"你運(yùn)行一下試試看。"
輸出如下:
group1 = LeetCode, length = 8 group2 = , length = 0你:"怎么會(huì)這樣?我期望的結(jié)果是 group1 等于 LeetCod,group2 等于 e 才對(duì)啊!"
NPC:"這是因?yàn)?e 仍然屬于 w 能匹配的范疇,正則表達(dá)式默認(rèn)會(huì)盡可能多地向后匹配,我們王國(guó)將其稱(chēng)之為 貪婪匹配。"
你:"貪婪匹配,聽(tīng)起來(lái)和貪心算法有異曲同工之妙。"
NPC:"沒(méi)錯(cuò),貪婪匹配和貪心算法原理是一致的。與之對(duì)應(yīng)的匹配方式叫做非貪婪匹配,非貪婪匹配會(huì)在能匹配目標(biāo)字符串的前提下,盡可能少的向后匹配。"
你:"那么,我要怎樣指定匹配方式為非貪婪匹配呢?"
NPC:"也很簡(jiǎn)單,在需要非貪婪匹配的正則表達(dá)式后面加個(gè) ? 即可表示非貪婪匹配。"
Pattern pattern = Pattern.compile("(w+?)(e*)"); Matcher matcher = pattern.matcher("LeetCode"); if (matcher.matches()) {String group1 = matcher.group(1);String group2 = matcher.group(2);System.out.println("group1 = " + group1 + ", length = " + group1.length());System.out.println("group2 = " + group2 + ", length = " + group2.length()); }運(yùn)行程序,輸出如下:
group1 = LeetCod, length = 7 group2 = e, length = 1你:"這里也用的是?,我記得之前?表示的是匹配 0 次或者 1 次,兩個(gè)符號(hào)不會(huì)混淆嗎?"
NPC:"不會(huì)混淆的,你仔細(xì)想一想就能明白了,如果只有一個(gè)字符,那就不存在貪婪不貪婪的問(wèn)題,如果匹配多次,那么表示非貪婪匹配的?前面必有一個(gè)標(biāo)志匹配次數(shù)的符號(hào)。所以不會(huì)出現(xiàn)混淆。"
你:"最后一個(gè)問(wèn)題,為什么這里沒(méi)有匹配成 group1 等于 L,group2 等于 ee...... 哦我明白了,如果這樣匹配的話,字符串LeetCode就無(wú)法和正則表達(dá)式匹配起來(lái)。怪不得非貪婪匹配的定義是在能匹配目標(biāo)字符串的前提下,盡可能少的向后匹配。"
NPC:"就是這個(gè)原理,看來(lái)你是真的完全明白了。"
第十一回 最終考驗(yàn)
NPC:"天下沒(méi)有不散的宴席,是時(shí)候說(shuō)再見(jiàn)了。雖然我能教你的,或是說(shuō)想與你探討的,還不止這些內(nèi)容,但授人以魚(yú)不如授人以漁,以后遇到正則相關(guān)的問(wèn)題,還是要靠你自己動(dòng)腦思考。"
你:"這么快就要告別了嗎?不知道為什么,竟然還有點(diǎn)舍不得......"
NPC:"我最后再出一道題考考你,你就可以從正則王國(guó)順利畢業(yè)了。來(lái)看下你的題目吧:我們王國(guó)有一個(gè)人口吃,請(qǐng)你幫忙矯正他。他今天說(shuō):肚...子。。好餓........,....早知道.....當(dāng).....初...。。。多.....刷.....點(diǎn)。。。力.....扣了.........!"
你:"ez,只需要用 str.replaceAll(__, __) 就可以解決了!"
互動(dòng)話題:
嘿,說(shuō)你呢!在留言區(qū)寫(xiě)下你的答案吧!
本文作者:Alpinist Wang
聲明:本文歸 “力扣” 版權(quán)所有,如需轉(zhuǎn)載請(qǐng)聯(lián)系。
總結(jié)
以上是生活随笔為你收集整理的java正则匹配英文句号_「正则表达式」王国奇遇记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ip 地址 192.168.1.255
- 下一篇: 文件自定义变量_awk 内置变量与自定义