java 正則表達(dá)式
正則表達(dá)式是一個(gè)非常強(qiáng)大的字符串處理工具,通過一種特殊的語法來描述一種模式,再通過模式可以完成字符串的匹配,萃取,替換等操作
簡(jiǎn)例
要判斷一個(gè)字符串是否是一個(gè)郵箱,可能需要很多的判斷邏輯,使用則表達(dá)式,只需要下面代碼即可
Pattern pattern = Pattern.compile("^([a-z0-9._%+-]+)@([a-z0-9.-]+)\\.[a-z]{2,4}$");
Matcher matcher = pattern.matcher("hatlonely@foxmail.com");
assertTrue(matcher.matches());
主要有 Pattern
對(duì)象和 Matcher
對(duì)象
-
Pattern
: 模式對(duì)象 -
Matcher
: 匹配的結(jié)果
部分匹配與完全匹配
Matcher
提供 matches
方法用于完全匹配,find
方法用于部分匹配,還提供 asPredicate
方法返回一個(gè)部分匹配的謂詞
Pattern
提供一個(gè)靜態(tài)方法 matches
用于完全匹配,這個(gè)方法會(huì)臨時(shí)構(gòu)造 Pattern
對(duì)象,如果 Pattern
會(huì)被多次重復(fù)使用,盡量別直接使用這種方法
assertTrue(Pattern.compile("hello").matcher("hello").matches()); // 完全匹配
assertTrue(Pattern.compile("hello").matcher("hello world").find()); // 部分匹配
assertTrue(Pattern.compile("hello").asPredicate().test("hello world")); // 部分匹配
assertTrue(Pattern.matches("hello", "hello")); // 完全匹配
通配符
符號(hào) | 含義 |
---|---|
c |
匹配單個(gè)字符 c |
. |
匹配所有單個(gè)字符,換行符 \n 除外 |
^ |
匹配字符串開始 |
$ |
匹配字符串結(jié)束 |
\b |
匹配字符界,字符和空白之間 |
\B |
匹配非字符界,字符和空白之間 |
| |
匹配前面表達(dá)式或者后面表達(dá)式 |
[charset] |
匹配任意括號(hào)內(nèi)的字符,可用 - 表示范圍,[a-z] 表示所有小寫字母 |
[^charset] |
匹配任意括號(hào)外的字符 |
\d |
匹配數(shù)字,相當(dāng)于 [0-9] |
\D |
匹配非數(shù)字,相當(dāng)于 [^0-9] |
\s |
匹配空白符,相當(dāng)于 [ \f\n\r\t\v] |
\S |
匹配非空白符,相當(dāng)于 [^ \f\n\r\t\v] |
\f |
匹配換頁(yè)符 |
\n |
匹配換行符 |
\r |
匹配回車 |
\t |
匹配字表符 |
\v |
匹配垂直制表符 |
\w |
匹配字符類字符,包括下劃線,相當(dāng)于 [A-Za-z0-9_] |
\W |
匹配非字符類字符,[^A-Za-z0-9_] |
\u |
匹配四位十六進(jìn)制數(shù)表示的 Unicode 字符,\u00A9 例如匹配 ? |
\x |
匹配兩位十六進(jìn)制數(shù)表示的 ascii 碼 |
限定符 | 含義 |
---|---|
* |
匹配前面字符或表達(dá)式 0 次或多次 |
+ |
匹配前面字符或表達(dá)式 1 次或多次 |
? |
匹配前面字符或表達(dá)式 0 次或1次,跟著其他限定詞后表示非貪婪匹配 |
{n} |
匹配前面字符或表達(dá)式 n 次 |
{n,} |
匹配前面字符或表達(dá)式至少 n 次 |
{n,m} |
匹配前面字符或表達(dá)式至少 n 次,至多 m 次 |
assertTrue(Pattern.matches("[0-9]*", ""));
assertTrue(Pattern.matches("[0-9]?", ""));
assertTrue(Pattern.matches("[0-9]?", "1"));
assertTrue(Pattern.matches("[0-9]+", "123"));
assertTrue(Pattern.matches("[0-9]{3}", "123"));
assertTrue(Pattern.matches("[0-9]{3,4}", "123"));
assertTrue(Pattern.matches("[0-9]{3,4}", "1234"));
assertTrue(Pattern.matches("[0-9]{3,}", "1234"));
assertTrue(Pattern.matches("\\d+", "123"));
assertTrue(Pattern.matches("\\s+", " \t"));
assertTrue(Pattern.matches("\\w+", "abc"));
assertTrue(Pattern.matches("(f|z)oo", "foo"));
assertTrue(Pattern.matches("(f|z)oo", "zoo"));
assertTrue(Pattern.matches(".*", "any string"));
捕獲分組
支持用小括號(hào) ()
將模式分組,Matcher
提供 group
方法獲取分組的內(nèi)容
符號(hào) | 含義 |
---|---|
(pattern) |
匹配分組并且捕獲子表達(dá)式 |
(?:pattern) |
匹配分組但是不捕獲子表達(dá)式 |
使用 groupCount
獲取捕獲分組的數(shù)量,由于下面例子中模式串中沒有小括號(hào),所以沒有捕獲的分組
Pattern pattern = Pattern.compile("^[a-z0-9]+@[a-z0-9.]+[.][a-z]{2,4}$");
Matcher matcher = pattern.matcher("hatlonely@foxmail.com");
assertTrue(matcher.matches());
assertEquals(matcher.groupCount(), 0);
使用 group
方法獲取分組的內(nèi)容,分組的編號(hào)以左括號(hào)為準(zhǔn),gruop(i)
返回第 i
個(gè)分組,group(0)
代表整個(gè)匹配串
Pattern pattern = Pattern.compile("^([a-z0-9]+)@(([a-z0-9.]+)[.]([a-z]{2,4}))$");
Matcher matcher = pattern.matcher("hatlonely@foxmail.com");
assertTrue(matcher.matches());
assertEquals(matcher.groupCount(), 4);
assertEquals(matcher.group(), "hatlonely@foxmail.com");
assertEquals(matcher.group(1), "hatlonely");
assertEquals(matcher.group(2), "foxmail.com");
assertEquals(matcher.group(3), "foxmail");
assertEquals(matcher.group(4), "com");
可以用 ?:
阻止捕獲,如下面代碼所示,foxmail.com
就沒有被捕獲
Pattern pattern = Pattern.compile("^([a-z0-9]+)@(?:([a-z0-9.]+)[.]([a-z]{2,4}))$");
Matcher matcher = pattern.matcher("hatlonely@foxmail.com");
assertTrue(matcher.matches());
assertEquals(matcher.groupCount(), 3);
assertEquals(matcher.group(), "hatlonely@foxmail.com");
assertEquals(matcher.group(0), "hatlonely@foxmail.com");
assertEquals(matcher.group(1), "hatlonely");
assertEquals(matcher.group(2), "foxmail");
assertEquals(matcher.group(3), "com");
預(yù)測(cè)
符號(hào) | 含義 |
---|---|
(?=pattern) |
正向預(yù)測(cè),非捕獲匹配,匹配結(jié)束 |
(?!pattern) |
反向預(yù)測(cè),非捕獲匹配,匹配結(jié)束 |
(?<=pattern) |
正向預(yù)測(cè),非捕獲匹配,匹配開始 |
(?<!pattern) |
反向預(yù)測(cè),非捕獲匹配,匹配開始 |
正向預(yù)測(cè)是指匹配開始或者結(jié)束的字符串需要匹配(或者反向預(yù)測(cè)不能匹配)分組,分組內(nèi)的模式僅用于預(yù)測(cè),不會(huì)出現(xiàn)在最終的匹配串中,這種匹配只能用于部分匹配,?=
,?!
匹配開始,?<=
,?<!
匹配結(jié)束
{
Pattern pattern = Pattern.compile("Windows (?=95|98|NT|2000)");
Matcher matcher = pattern.matcher("Windows 2000");
assertTrue(matcher.find());
assertEquals(matcher.group(), "Windows ");
assertEquals(matcher.groupCount(), 0);
}
{
Pattern pattern = Pattern.compile("Windows (?!95|98|NT|2000)");
Matcher matcher = pattern.matcher("Windows vista");
assertTrue(matcher.find());
assertEquals(matcher.group(), "Windows ");
assertEquals(matcher.groupCount(), 0);
}
{
Pattern pattern = Pattern.compile("(?<=95|98|NT|2000) Windows");
Matcher matcher = pattern.matcher("2000 Windows");
assertTrue(matcher.find());
assertEquals(matcher.group(), " Windows");
assertEquals(matcher.groupCount(), 0);
}
{
Pattern pattern = Pattern.compile("(?<!95|98|NT|2000) Windows");
Matcher matcher = pattern.matcher("vista Windows");
assertTrue(matcher.find());
assertEquals(matcher.group(), " Windows");
assertEquals(matcher.groupCount(), 0);
}
反向引用
符號(hào) | 含義 |
---|---|
\1 |
匹配捕獲匹配到的反向引用,\2 表示第二個(gè)反向引用 |
前面通過捕獲獲得的分組可以再后面引用,比如 (\w+) \1
表示兩個(gè)重復(fù)的單詞
assertTrue(Pattern.matches("(\\w+) \\1", "ab ab"));
assertTrue(Pattern.matches("(\\w+) \\1", "abc abc"));
assertFalse(Pattern.matches("(\\w+) \\1", "abc def"));
正則替換
String
和 Pattern
都提供了 replaceAll
和 replaceFirst
方法來作正則替換,替換串中可以 $i
來獲取正則匹配捕獲的分組
Pattern pattern = Pattern.compile("^([a-z0-9]+)@(?:([a-z0-9.]+)[.]([a-z]{2,4}))$");
assertEquals(pattern.matcher("hatlonely@foxmail.com").replaceAll(
"$0 $1 $2 $3"
), "hatlonely@foxmail.com hatlonely foxmail com");
assertEquals("hatlonely@foxmail.com".replaceAll(
"^([a-z0-9]+)@(?:([a-z0-9.]+)[.]([a-z]{2,4}))$", "$0 $1 $2 $3"
), "hatlonely@foxmail.com hatlonely foxmail com");
搜索所有匹配
每次 find 調(diào)用都會(huì)匹配目標(biāo)串中的一個(gè)匹配,通過多次 find
調(diào)用,可以找出目標(biāo)串中所有的匹配
String str = "abab x acac y aeae";
Pattern pattern = Pattern.compile("(\\w+)\\1");
Matcher matcher = pattern.matcher(str);
List<String> li = new ArrayList<>();
while (matcher.find()) {
li.add(matcher.group());
assertThat(str.substring(matcher.start(), matcher.end()), equalTo(matcher.group()));
}
assertThat(li, equalTo(List.of("abab", "acac", "aeae")));