Skip to content

正则表达式

4141字约14分钟

java正则表达式

2024-10-24

1.Properties类

1.将此属性列表打印到指定的输出流。

list(Properties out)

2.load(InputStrean in) 从输入字节流读取属性列表(键和元素对)

3.load(Reader reader) 以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。

4.loadFromXML(InputStream in) 将指定输入流中的XML文档表示的所有属性加载到此属性表中。

key和value为String类型,可以从文件读取内容到集合

正则表达式

1.定义

用来处理字符串正确规则的表达式

2.作用

1.验证字符串

String.matches(regex)

2.替换分割字符串

替换:String.replaceAll(regex, "*")

分割:String.split(regex)

3.捕获子字符串

语法糖

3.各类字符

1.字符类

\xhh 表示16进制

\ uhhhh 表示Unicode字符

\ .表示.

.代表任意一个字符

2.一个字符

1.[123]表示123中的任意一个字符

[^123]表示非123中的任意一个字符

	private static void test1() {
		// [123]表示123中的任意一个字符
		String regex = "[123]";
		System.out.println("1".matches(regex));// true
		System.out.println("2".matches(regex));// true
		System.out.println("3".matches(regex));// true
		System.out.println("a".matches(regex));// false
		System.out.println();
		// [^123]表示非123中的任意一个字符
		regex = "[^123]";
		System.out.println("1".matches(regex));// false
		System.out.println("2".matches(regex));// false
		System.out.println("3".matches(regex));// false
		System.out.println("a".matches(regex));// true
		System.out.println();
	}

image-20211006201420510

3.一类字符

[a-z]表示任意一个小写字母,不包括'-'

[a-zA-Z]表示任意一个字母

[a-zA-Z0-9]表示所有英文字母和数字

	public static void test2() {
		String regex;
		// [a-z]表示任意一个小写字母,不包括'-'
		regex = "[a-z]";
		System.out.println("a".matches(regex));// true
		System.out.println("z".matches(regex));// true
		System.out.println("A".matches(regex));// false
		System.out.println("1".matches(regex));// false
		System.out.println("-".matches(regex));// false
		System.out.println();
		// [a-zA-Z]表示任意一个字母
		regex = "[a-zA-Z]";
		System.out.println("a".matches(regex));// true
		System.out.println("z".matches(regex));// true
		System.out.println("A".matches(regex));// true
		System.out.println("1".matches(regex));// false
		System.out.println();
		// [a-zA-Z0-9]表示所有英文字母和数字
		regex = "[a-zA-Z0-9]";
		System.out.println("a".matches(regex));// true
		System.out.println("z".matches(regex));// true
		System.out.println("A".matches(regex));// true
		System.out.println("1".matches(regex));// true
		System.out.println("哈哈哈".matches(regex));// false
		System.out.println();
	}

image-20211006201457247

4.预定义字符

\d表示任意一个数字[0-9]

\D表示任意一个非数字字符[^0-9]

\w表示任意一个单词字符[a-zA-Z_0-9]

\W表示任意一个非单词字符[^a-zA-Z_0-9]

\s表示任意一个非可视字符

\s与""匹配为false,\S与""匹配为false

\S表示任意一个可视字符

	private static void test3() {
		String regex;
		// \\d表示任意一个数字[0-9]
		regex = "\\d";
		System.out.println("1".matches(regex));// true
		System.out.println("2".matches(regex));// true
		System.out.println("a".matches(regex));// false
		System.out.println("b".matches(regex));// false
		System.out.println();
		// \\D表示任意一个非数字字符[^0-9]
		regex = "\\D";
		System.out.println("1".matches(regex));// false
		System.out.println("2".matches(regex));// false
		System.out.println("a".matches(regex));// true
		System.out.println("b".matches(regex));// true
		System.out.println();
		// \\w表示任意一个单词字符[a-zA-Z_0-9]
		regex = "\\w";
		System.out.println("1".matches(regex));// true
		System.out.println("2".matches(regex));// true
		System.out.println("a".matches(regex));// true
		System.out.println("b".matches(regex));// true
		System.out.println("_".matches(regex));// true
		System.out.println("%".matches(regex));// false
		System.out.println();
		// \\W表示任意一个非单词字符[^a-zA-Z_0-9]
		regex = "\\W";
		System.out.println("1".matches(regex));// false
		System.out.println("2".matches(regex));// false
		System.out.println("a".matches(regex));// false
		System.out.println("b".matches(regex));// false
		System.out.println("_".matches(regex));// false
		System.out.println("%".matches(regex));// true
		System.out.println();
		// \\s表示任意一个非可视字符
		regex = "\\s";
		System.out.println(" ".matches(regex));// true
		System.out.println("\t".matches(regex));// true
		System.out.println("\n".matches(regex));// true
		System.out.println("\r".matches(regex));// true
		System.out.println("\0".matches(regex));// false
		System.out.println("a".matches(regex));// false
		System.out.println();
		// \\s与""匹配为false,\\S与""匹配为false
		System.out.println("".matches(regex));// false
		System.out.println("".matches("\\S"));// false
		System.out.println();
		// \\S表示任意一个可视字符
		regex = "\\S";
		System.out.println(" ".matches(regex));// false
		System.out.println("\t".matches(regex));// false
		System.out.println("\n".matches(regex));// false
		System.out.println("\r".matches(regex));// false
		System.out.println("\0".matches(regex));// true
		System.out.println("a".matches(regex));// true
		System.out.println();
	}

image-20211006201557398

image-20211006201614658

5.字符的匹配区域

并集,[a-d[A-D]]相当于[a-dA-D]

交集,[a-z[b-d]]相当于[b-d]

差集,[a-z[^b-d]]相当于[ae-z]

[\u4E00-\u9FA5]表示大部分汉字

	private static void test4() {
		// 并集,[a-d[A-D]]相当于[a-dA-D]
		String regex;
		regex = "[a-d[A-D]]";
		System.out.println("b".matches(regex));// true
		System.out.println("e".matches(regex));// false
		System.out.println("B".matches(regex));// true
		System.out.println("E".matches(regex));// false
		System.out.println();
		// 交集,[a-z[b-d]]相当于[b-d]
		regex = "[a-z&&[b-d]]";
		System.out.println("c".matches(regex));// true
		System.out.println("e".matches(regex));// false
		System.out.println();
		// 差集,[a-z[^b-d]]相当于[ae-z]
		regex = "[a-z&&[^b-d]]";
		System.out.println("c".matches(regex));// false
		System.out.println("e".matches(regex));// true
		System.out.println();
		// [\\u4E00-\\u9FA5]表示大部分汉字
		regex = "[\\u4E00-\\u9FA5]";
		System.out.println("A".matches(regex));// false
		System.out.println("".matches(regex));// true
		System.out.println();
	}

image-20211006201643787

6.字符数量的表示

a{3}表示连续的三个a

a{0,3}表示0到3个a

a{0,}表示0到多个a

a{0,3}表示1到3个a

a*表示任意多个a

a?表示0或1个a

a+表示1到任意多个a

	private static void test5() {
		// a{3}表示连续的三个a
		String regex;
		regex = "a{3}";
		System.out.println("a".matches(regex));// false
		System.out.println("aa".matches(regex));// false
		System.out.println("aaa".matches(regex));// true
		System.out.println("aaaa".matches(regex));// false
		System.out.println();
		// a{0,3}表示0到3个a
		regex = "a{0,3}";
		System.out.println("".matches(regex));// true
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// true
		System.out.println("aaa".matches(regex));// true
		System.out.println("b".matches(regex));// false
		System.out.println();
		// a{0,}表示0到多个a
		regex = "a{0,}";
		System.out.println("".matches(regex));// true
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// true
		System.out.println("aaaaaaaa".matches(regex));// true
		System.out.println();
		// a{0,3}表示1到3个a
		regex = "a{1,3}";
		System.out.println("".matches(regex));// false
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// true
		System.out.println("aaa".matches(regex));// true
		System.out.println("b".matches(regex));// false
		System.out.println();
		// a*表示任意多个a
		regex = "a*";
		System.out.println("".matches(regex));// true
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// true
		System.out.println("aaa".matches(regex));// true
		System.out.println("b".matches(regex));// false
		System.out.println();
		// a?表示0或1个a
		regex = "a?";
		System.out.println("".matches(regex));// true
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// false
		System.out.println();
		// a+表示1到任意多个a
		regex = "a+";
		System.out.println("".matches(regex));// false
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// true
		System.out.println();
	}

image-20211006201803831

7.字符边界的条件

^a表示一行字符的开头中的a

a$表示一行字符的结尾中的a

a表示一行字符中的所有a

\b表示单词边界处

\ba表示单词第一个字母为a

a\b表示单词末尾字母为a

\B表示单词边界处

\A表示一个字符串的开头

	private static void test6() {
		String regex;
		// ^a表示一行字符的开头中的a
		regex = "^a";
		System.out.println("abcabc".replaceAll(regex, "*"));// *bcabc
		// a$表示一行字符的结尾中的a
		regex = "a$";
		System.out.println("abcabca".replaceAll(regex, "*"));// abcabc*
		// a表示一行字符中的所有a
		regex = "a";
		System.out.println("abcabca".replaceAll(regex, "*"));// *bc*bc*
		// \\b表示单词边界处
		// \\ba表示单词第一个字母为a
		regex = "\\ba";
		System.out.println("abc \nabca".replaceAll(regex, "*"));// *bc \n*bca
		// a\\b表示单词末尾字母为a
		regex = "a\\b";
		System.out.println("abca abca".replaceAll(regex, "*"));// abc* abc*
		// \\B表示单词边界处
		regex = "\\Ba";
		System.out.println("abc abca".replaceAll(regex, "*"));// abc abc*
		regex = "a\\B";
		System.out.println("abca \nabca".replaceAll(regex, "*"));// *bca \n*bca
		// \\A表示一个字符串的开头
		regex = "\\Aa";
		System.out.println("abc \nabca".replaceAll(regex, "*"));// *bc \nabca
	}

image-20211006201837010

8.逻辑

[a|b]表示字符a或b

[ab]表示a或b

	private static void test7() {
		// [a|b]表示字符a或b
		String regex;
		regex = "[a|b]";
		System.out.println("".matches(regex));// false
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// false
		System.out.println("aaa".matches(regex));// false
		System.out.println("b".matches(regex));// true
		System.out.println();
		// [ab]表示a或b
		regex = "[ab]";
		System.out.println("a".matches(regex));// true
		System.out.println("aa".matches(regex));// false
		System.out.println("ab".matches(regex));// false
		System.out.println("b".matches(regex));// true
		System.out.println("abb".matches(regex));// false
		System.out.println();
	}

image-20211006202047615

4.正则表达式的简化(语法糖)

1.捕获字符串

(\B\d\.)一个小括号的表达式称为一个捕获组

$0表示全部捕获组,$1表示第一个捕获组.......

正则表达式支持字符

创建正则表达式就是创建一个特殊的字符串。正则表达式所支持的合法字符如表 1 所示。

字符解释
X字符x(x 可代表任何合法的字符)
\0mnn八进制数 0mnn 所表示的字符
\xhh十六进制值 0xhh 所表示的字符
\uhhhh十六进制值 0xhhhh 所表示的 Unicode 字符
\t制表符(“\u0009”)
\n新行(换行)符(‘\u000A’)
\r回车符(‘\u000D’)
\f换页符(‘\u000C’)
\a报警(bell)符(‘\u0007’)
\eEscape 符(‘\u001B’)
\cxx 对应的的控制符。例如,\cM匹配 Ctrl-M。x 值必须为 A~Z 或 a~z 之一。

除此之外,正则表达式中有一些特殊字符,这些特殊字符在正则表达式中有其特殊的用途,比如前面介绍的反斜线\

如果需要匹配这些特殊字符,就必须首先将这些字符转义,也就是在前面添加一个反斜线\。正则表达式中的特殊字符如表 2 所示。

特殊字符说明
$匹配一行的结尾。要匹配 $ 字符本身,请使用\$
^匹配一行的开头。要匹配 ^ 字符本身,请使用\^
()标记子表达式的开始和结束位置。要匹配这些字符,请使用\(\)
[]用于确定中括号表达式的开始和结束位置。要匹配这些字符,请使用\[\]
{}用于标记前面子表达式的出现频度。要匹配这些字符,请使用\{\}
*指定前面子表达式可以出现零次或多次。要匹配 * 字符本身,请使用\*
+指定前面子表达式可以出现一次或多次。要匹配 + 字符本身,请使用\+
?指定前面子表达式可以出现零次或一次。要匹配 ?字符本身,请使用\?
.匹配除换行符\n之外的任何单字符。要匹配.字符本身,请使用\.
\用于转义下一个字符,或指定八进制、十六进制字符。如果需匹配\字符,请用\\
|指定两项之间任选一项。如果要匹配字符本身,请使用|

将上面多个字符拼起来,就可以创建一个正则表达式。例如:

"\u0041\\" // 匹配 A
"\u0061\t" // 匹配a<制表符> "\?\[" // 匹配?[

注意:可能大家会觉得第一个正则表达式中怎么有那么多反斜杠?这是由于 Java 字符串中反斜杠本身需要转义,因此两个反斜杠(\)实际上相当于一个(前一个用于转义)。

上面的正则表达式依然只能匹配单个字符,这是因为还未在正则表达式中使用“通配符”,“通配符”是可以匹配多个字符的特殊字符。正则表达式中的“通配符”远远超出了普通通配符的功能,它被称为预定义字符,正则表达式支持如表 3 所示的预定义字符。

预定义字符说明
.可以匹配任何字符
\d匹配 0~9 的所有数字
\D匹配非数字
\s匹配所有的空白字符,包括空格、制表符、回车符、换页符、换行符等
\S匹配所有的非空白字符
\w匹配所有的单词字符,包括 0~9 所有数字、26 个英文字母和下画线_
\W匹配所有的非单词字符

上面的 7 个预定义字符其实很容易记忆,其中:

  • d 是 digit 的意思,代表数字。
  • s 是 space 的意思,代表空白。
  • w 是 word 的意思,代表单词。
  • d、s、w 的大写形式恰好匹配与之相反的字符。

有了上面的预定义字符后,接下来就可以创建更强大的正则表达式了。例如:

c\wt // 可以匹配cat、cbt、cct、cOt、c9t等一批字符串 \d\d\d-\d\d\d-\d\d\d\d // 匹配如 000-000-0000 形式的电话号码

在一些特殊情况下,例如,若只想匹配 a~f 的字母,或者匹配除 ab 之外的所有小写字母,或者匹配中文字符,上面这些预定义字符就无能为力了,此时就需要使用方括号表达式,方括号表达式有如表 4 所示的几种形式。

方括号表达式说明
表示枚举例如[abc]表示 a、b、c 其中任意一个字符;[gz]表示 g、z 其中任意一个字符
表示范围:-例如[a-f]表示 a~f 范围内的任意字符;[\\u0041-\\u0056]表示十六进制字符 \u0041 到 \u0056 范围的字符。范围可以和枚举结合使用,如[a-cx-z],表示 ac、xz 范围内的任意字符
表示求否:^例如[^abc]表示非 a、b、c 的任意字符;[^a-f]表示不是 a~f 范围内的任意字符
表示“与”运算:&&例如 [a-z&&[def]]是 a~z 和 [def] 的交集,表示 d、e f[a-z&&^bc]]是 a~z 范围内的所有字符,除 b 和 c 之外 [ad-z] [a-z&&[m-p]]是 a~z 范围内的所有字符,除 m~p 范围之外的字符
表示“并”运算并运算与前面的枚举类似。例如[a-d[m-p]]表示 [a-dm-p]

方括号表达式比前面的预定义字符灵活多了,几乎可以匹配任何字符。例如,若需要匹配所有的中文字符,就可以利用 [\u0041-\u0056] 形式——因为所有中文字符的 Unicode 值是连续的,只要找出所有中文字符中最小、最大的 Unicode 值,就可以利用上面形式来匹配所有的中文字符。

正则表达式还支持圆括号,用于将多个表达式组成一个子表达式,圆括号中可以使用或运算符|。例如,正则表达式“((public)|(protected)|(private))”用于匹配 Java 的三个访问控制符其中之一。

除此之外,Java 正则表达式还支持如表 5 所示的几个边界匹配符。

边界匹配符说明
^行的开头
$行的结尾
\b单词的边界
\B非单词的边界
\A输入的开头
\G前一个匹配的结尾
\Z输入的结尾,仅用于最后的结束符
\z输入的结尾

前面例子中需要建立一个匹配 000-000-0000 形式的电话号码时,使用了 \d\d\d-\d\d\d-\d\d\d\d 正则表达式,这看起来比较烦琐。实际上,正则表达式还提供了数量标识符,正则表达式支持的数量标识符有如下几种模式。

  • Greedy(贪婪模式):数量表示符默认采用贪婪模式,除非另有表示。贪婪模式的表达式会一直匹配下去,直到无法匹配为止。如果你发现表达式匹配的结果与预期的不符,很有可能是因为你以为表达式只会匹配前面几个字符,而实际上它是贪婪模式,所以会一直匹配下去。
  • Reluctant(勉强模式):用问号后缀(?)表示,它只会匹配最少的字符。也称为最小匹配模式。
  • Possessive(占有模式):用加号后缀(+)表示,目前只有 Java 支持占有模式,通常比较少用。

三种模式的数量表示符如表 6 所示。

贪婪模式勉强模式占用模式说明
X?X??X?+X表达式出现零次或一次
X*X*?X*+X表达式出现零次或多次
X+X+?X++X表达式出现一次或多次
XX{n}?X{n}+X表达式出现 n 次
XX{n,}?X{n,}+X表达式最少出现 n 次
XX{n,m}?X{n,m}+X表达式最少出现 n 次,最多出现 m 次

关于贪婪模式和勉强模式的对比,看如下代码:

String str = "hello,java!"; // 贪婪模式的正则表达式 System.out.println(str.replaceFirst("\w*" , "■")); //输出■,java! // 勉强模式的正则表达式 System.out.println(str.replaceFirst("\w*?" , "■"")); //输出■hello, java!

当从“hello java!”字符串中查找匹配\\w*子串时,因为\w*使用了贪婪模式,数量表示符*会一直匹配下去,所以该字符串前面的所有单词字符都被它匹配到,直到遇到空格,所以替换后的效果是“■,Java!”;如果使用勉强模式,数量表示符*会尽量匹配最少字符,即匹配 0 个字符,所以替换后的结果是“■hello,java!”。