前言
java中的正则表达式,从认识正则表达式的常用类,底层实现原理,到元字符的使用,元字符的使用细节及注意事项,分组引用及反向引用实例,Matcher类常用的其它方法,String类中的正则使用,及常用的正则表达式(摘选),
带你从0到1学会正则表达式,一篇文章足以~
一、正则表达式的三个常用类
1.Pattern类
Pattern 对象是一个正则表达式对象。Pattern 类没有公共构造方法。需要使用 Pattern 类 compile 方法接受一个正则表达式,获得 Pattern对象
String regex = "\\d{4}"; // 正则表达式
Pattern pattern = Pattern.compile(String regex); // 接受正则表达式(字符串),构造正则表达式对象
// 匿名对象
Pattern.compile(String regex);
2.Matcher类
在有了正则表达式对象之后,我们需要匹配器,即 Matcher 类的对象
Matcher 对象是对输入字符串解释和匹配的引擎。Matcher 类没有公共构造方法。需要使用 Pattern 对象的 matcher 方法接受匹配的内容,获得 Matcher 对象
Matcher matcher = pattern.matcher(String content);
// 匿名对象
Pattern.compile(String regex).matcher(String content);
3.PatternSyntaxException类
PatternSyntaxException类是一个非强制异常类,它表示正则模式中的语法错误
二、正则表达式的底层实现
有了匹配器后,就可以进行对字符串的检索了。
String content = "1234ab123cd56789"; // 检索的内容
String regex = "\\d{4}"; // 正则表达式
Pattern pattern = Pattern.compile(regex); // 获得正则表达式对象
Matcher matcher = Pattern.matcher(content); // 获得匹配器
// 开始对字符串从头到尾进行检索
while(matcher.find()){
// 将获取的满足的子字符串输出
System.out.println(matcher.group());
}
/* 输出结果 */
1234
5678
1.Matcher.find()和Matcher.group()方法
下边介绍匹配器对字符串的检索时的 find() 方法和 group() 方法的底层实现
Matcher类中有两个属性: int[] groups、int oldlast
int[] gorups 在执行 find() 方法时用来存放匹配到的字符串的 开始和结束后一位下标。并在需要时通过调用 gorup() 方法来获取下标对字符串进行截取。
int oldlast 存放匹配字符结束后一位的下标,作为下一次匹配的开始位置
2.Matcher.find()和Matcher.group()实现过程
String content = "1234abcd5678opq";
String regex = "\\d\\d\\d\\d"; // \\d表示一位 0-9 的数字
...
// 匹配content中的四位数字
while(matcher.find()){
// 用 group()输出匹配到的子字符串
System.out.println(matcher.group());
}
首先,find()方法会对字符串依次进行检索,
1.如果末匹配到,返回false.结束匹配
2.如果匹配到指定规则子字符串:如上面的例子,匹配 1234 时,会将子字符串1234(在content)中的初始位置, 也就是字符1 对应的 下标 存入 groups[0],将末尾位置 字符4 对应的 下标+1 存入到 group[1]中;
并在需要时用 group() 方法获取 匹配到的子字符串,group() 方法会返回对content字符串中groups[0]和group[1]之间的子字符串,也就是对其进行截取.
即: group() = content.subString( groups[0],gorups[1] );同时还会记录当前匹配到的子字符中末尾后一位, 即将 groups[1] 存入到 oldlast 中 (用于下一次匹配的开始)
随后进行下一次匹配,也就是执行第二次while()循环,以 oldlast 为开始位置 往后进行匹配,同样的,在匹配到子字符串 如 5678 时,初始 字符5 对应的下标存入 groups[0],末尾 字符8 对应的下标存入到groups[1]中(此次会将上一次的匹配结果替换掉),将当前匹配到的子字符中末尾后一位存入到 oldlast 中。
当下一次匹配进行,匹配到字符串(content)的结尾,末找到指定的子字符串时,返回false,结束 while()循环
3.Matcher.group()方法中的参数
grop() 方法是带有参数的,如: 0,1,2,3…
group(0)获取的是 1234,即 匹配到的子字符串整体;不带参数的group()方法其实也是默认参数为0;
源码如下:
public String group() {
return group(0);
}当我们对正则表达式进行分组时,即添加小括号,如:
String regex = (\d\d) (\d\d);
String content = “1234abcd”;
group(1)获取的是 整体中的第一组,也就是正则表达式中第一个括号中的内容: 12
依次的,group(2)获取第二组: 34; …
注意:如果想要获取超过范围的分组,会报错
三、正则表达式:元字符
Pattern.matches(String regex,String content);方法
用正则regex去匹配 整个 字符串content
1.字符匹配符
正则: .
. (小数点) 则表示“\n”和"\r"之外的任何单个字符
… :则表示任意四个字符
Pattrn.matches("....","1234");
Pattrn.matches("....","abcd");
// true
Pattrn.matches("....","12345");
// false
正则: \d
\d表示一个数字
aaa\d:检验是否以aaa开头,且以一个数字结尾的字符串
aaa\dbbb:aaa和bbb中间有一个数字
aaa\d\d:aaa后面跟2个数字
---
Pattrn.matches("aaa\\d","aa1"); // flase
---
Pattrn.matches("aaa\\d","aaa11"); // flase
---
Pattrn.matches("aaa\\d","aaa9") // true
注意:在Java定义的正则里,由于一个 \表示的是字符串转义,因此在Java定义带有\的元字符时,还需要多写一个 \,即 \ \,至于其他语言,自己可查阅相关资料进行了解。
正则: \D
\D 表示一个非数字,它和上面 \d 的意思恰好相反。
\D\D\D: 则表示一个长度为3,不包含数字的字符串。
Pattrn.matches("\\D\\D\\D",11); // false
Pattrn.matches("\\D\\D\\D","aaa"); // true
111\D222:则表示111和222中间,必须包含一个非数字。
Pattrn.matches("111\D222","1110222"); // false
Pattrn.matches("111\D222","111+222"); // true
正则: \w
\w 表示一个字母(大小写均可)、数字,或下划线。
12\w45:则表示12和45中间是一个字母,数字,或下划线。
Pattrn.matches("12\\w45","12345");
Pattrn.matches("12\\w45","12a45");
Pattrn.matches("12\\w45","12_45");
// true
Pattrn.matches("12\\w45","12+45");
// false
正则: \W
\W 与 \w 相反,表示这个位置的字符既不是字母、数字,也不是下划线。
也就是:特殊符号(除下划线),或者空格等满足。
12\w45:则表示12和45中间是一个非字母,非数字,或非下划线。
Pattrn.matches("12\\W45","12345");
Pattrn.matches("12\\W45","12A45");
Pattrn.matches("12\\W45","12_45");
// false
Pattrn.matches("12\\W45","12+45");
Pattrn.matches("12\\W45","12 45");
// true
正则:[abc]
[ ] 表示匹配其中任意一个字符。
a[bcd]e:则表示a和e的中间须是b,或c,或d其中的一个
Pattrn.matches("a[bcd]e","abe");
Pattrn.matches("a[bcd]e","ace");
// true
Pattrn.matches("a[bcd]e","a6e");
// false
注意:用 | 表示其中之一,他可以是字符,也可以是字符串。而只用中括号时,则只表示其中一个字符。
正则:[^abc]
[^abc]与 [abc] 相反,表示不与中括号里的任意字符匹配
a[^bcd]e:则表示a和e的中间除b,c,d这三个字符外,其他的字符都满足。
Pattrn.matches("a[\^abc]e","a1e"); // true
Pattrn.matches("a[\^abc]e","aae"); // false
正则:[a-z]
[值1-值2] 则表示值1到值2中间的所有字符都满足(包括值1和值2)。常用该正则来表示大小写字母范围,数字范围。
a[b-d]e:等同于 a[bcd]e,因为 b-d 其实就是b,c,d三个数。
Pattrn.matches("a[b-d]e","abe"); // true
Pattrn.matches("a[b-d]e","a1e"); // false
a[0-9]e:则表示ae中间是一个数字等同于 a\de(前面说过\d表示一个数字)
Pattrn.matches("a[0-9]e","ane"); // false
Pattrn.matches("a[0-9]e","a5e"); // true
正则:[^a-z]
与 [值1-值2] 相反,[^值1-值2] 则表示除值1和值2之外的所有字符,都可以满足。
a[^1-3]e:则表示a和e中间的字符,只要不是1,2,3,则都满足。
Pattrn.matches("a[^1-3]e","a4e"); // true
Pattrn.matches("a[^1-3]e","a1e"); // false
2.选择匹配符
正则:|
| (竖线) 则表示或的关系,表示检测的字符串须满足其中一个时,才符合条件
aa|bb|cc:则表示输入的字符串须是aa,或bb,或cc其中的一个。
Pattrn.matches("aa|bb|cc","aa");
Pattrn.matches("aa|bb|cc","bb");
Pattrn.matches("aa|bb|cc","cc");
// true
Pattrn.matches("aa|bb|cc","1");
// false
xx(aa|bb|cc)yy:则表示输入的字符串须是xx开头yy结尾且中间是aa、bb或cc
Pattrn.matches("xx(aa|bb|cc)yy","xxaayy");
Pattrn.matches("xx(aa|bb|cc)yy","xxbbyy");
Pattrn.matches("xx(aa|bb|cc)yy","xxccyy");
// true
Pattrn.matches("xx(aa|bb|cc)yy","xx11yy");
// false
3.限定符
正则:*
表示匹配前面的子表达式任意次。
abc*de:**表示 ab 和 de 之间有任意个数(包括0)c **
a(bc)*de:**表示 ab 和 de 之间有任意个数(包括0)bc **
Pattrn.matches("abc*de","abde");
Pattrn.matches("abc*de","abcde");
Pattrn.matches("abc*de","abccde");
// true
Pattrn.matches("abc*de","ade");
// false
正则:+
匹配前面的子表达式一次或多次 (次数 >= 1,即至少1次)
abc+de:ab 和 de 之间至少有一个 c 。
Pattrn.matches("abc?de","abcde");
Pattrn.matches("abc?de","abccccde");
// true
Pattrn.matches("abc?de","abde");
// false
a(bc)+de:a 和 de 之间至少有一个bc 。
Pattrn.matches("a(bc)?de","abcde");
Pattrn.matches("a(bc)?de","abcbcde");
// true
Pattrn.matches("a(bc)?de","abde");
// false
正则:?
? 表示匹配前面的子表达式零次或一次。
abc?de: 表示可匹配的字符串为 abde (匹配0次c) 或 abcde (匹配1次c)
Pattrn.matches("abc?de","abde");
Pattrn.matches("abc?de","abcde");
// true
Pattrn.matches("abc?de","ade");
// false
正则:{n}
这里的 n 是一个非负整数。匹配确定的前面的子表达式 n 次。
abc{3}de:表示 ab 和 de 之间有3个c。相当于abcccde
Pattrn.matches("abc{3}de","abde");
Pattrn.matches("abc{3}de","abcde");
Pattrn.matches("abc{3}de","abccde");
// false
Pattrn.matches("abc{3}de","abcccde");
// true
ab(xx|yy){3}de:表示 ab 和 de 之间有 xx 或 yy 的个数, 一起合计为3个。
Pattrn.matches("ab(xx|yy){3}de","abxxxxxxde");
Pattrn.matches("ab(xx|yy){3}de","abyyyyyyde");
Pattrn.matches("ab(xx|yy){3}de","abxxyyxxde");
// true
Pattrn.matches("ab(xx|yy){3}de","abcxxyyde");
// true
正则:{n,}
这里的 n 是一个非负整数。指匹配的子字符串长度不小于n。
abc{3,}de:表示 ab 和 de 至少有3个c。
Pattrn.matches("abc{3,}de","abde");
Pattrn.matches("abc{3,}de","abcde");
Pattrn.matches("abc{3,}de","abccde");
// false
Pattrn.matches("abc{3}de","abcccde");
Pattrn.matches("abc{3}de","abccccde");
// true
正则:{n,m}
m和n均为非负整数,其中 n<=m。最少匹配 n 次且最多匹配 m 次。
abc{2,3}de:表示 ab 和 de 之间有 2 到 3 个 c。
Pattrn.matches("abc{2,3}de","abccde");
Pattrn.matches("abc{2,3}de","abcccde");
// true
Pattrn.matches("abc{2,3}de","abcde");
// false
4.分组和反向引用符
(Pattern)
非命名捕获,编号为 0 的第一个捕获是由整个子字符串,其它捕获结果根据 左括号 的顺序从1自动编号
如 (\\d\d)(\\d\\d) 去匹配 “abcd1234”
- group(0) = “1234”;
- group(1) = “12”;
- group(2) = “34”;
(?<组名>Pattern)
命名捕获,即在分组的同给其命名,命名后分组可以通过gruop(name)来获取
如(?<分组1>\\d\d)(?<分组2>\\d\\d) 去匹配 “abcd1234”
- group(0) = “1234”;
- group(“分组1”) = “12”;
- group(“分组2”) = “34”;
引用(\\n)
(内部引用)对已分组的正则表达式进行引用,(n表示一个数字)如:\\1表示第一个括号里的内容,以此类推
如正则表达式 (\\d)\\1(\\d)\\2 表示的是 第一个数字和第二个数字相同,第三个数字和第四个数字相同
- 1122、5566 7788 等满足上式正则表达式、
反向引用($n)
(外部引用),与内部引用使用方法类似,只不过是在regex的外部使用
如Matcher类中的replace(String s)方法
String content = "122333444455555";
String regex = "(\\d)\\1*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
// 使用replaceAll方法,
// 将content中含有regex的内容全都替换成 $1,即(\\d)
String newstring = matcher.replaceAll("$1");
System.out.println(newstring);
/* newString = "12345"; */
5.定位符
正则: ^
^ 表示以指定的字符开头
如 ^\\d{3}+[a-z]* 去匹配:
- a123abc,匹配不成功
- 123abc,输出整个字符串abc
正则: $
与 ^ 类似,$ 则是以指定字符结尾
如 \\d{3}+[a-z]*$ 去匹配
- a123abc6,匹配不成功
- 123abc,输出整个字符串abc
关于 ^ 和 $ 的使用场景
通常,我们在使用Matcher.find()方法时,是以正则表达式String regex 去匹配String content中的子字符串,即字符串content中只要有一部分满足regex即匹配成功返回true;
当使用了 ^ 时,表示的不是字符串content中的子字符串以什么开头,而是content的开头是什么字符。$ 同样如此;下面有几个例子供大家理解:用Matcher.find()方法,regex = “^\d{2}” 去匹配:
- content = “abc123”; 匹配不成功,
- content = “1abc123” 匹配不成功,
- content = “12abc123”; 返回 “123abc123”;
这里12a,12ab,12abc…同样满足,但是因为java匹配默认贪婪匹配.当我们给regex正则表达式的头尾分别加上 ^ ,$ 时,此时用find()方法进行匹配不再时部分匹配,表示的是将对整个字符串content和regex进行匹配,即content == regex?
6.注意
元字符使用时的一些细节
1.连字符只能用在【】内即: 【A-Z】,在【】外面或如【a-】、【\\w-】中的边字符都只是一个普通字符
2.像. ? 这些字符在【】中也是一个普通字符
3.注意: java匹配默认贪婪匹配,即尽可能的多的,如:\d{3,} 当字符串中如:4456,会匹配 4456 而不是 445
4.要匹配内容中的字符 \ 时,因为 java和正则的转义,正则表达式要写为:
String regex = “\\\\” 才能将 \ 表示成一个普通字符
四、Matcher类其它常用方法
Matcher.start()
返回 gruops[0],即匹配到的子字符串初始字符下标
Matcher.end()
返回 gruops[1],即匹配到的子字符串末尾字符下标+1
Matcher.matchers()
将字符串整体与 String regex 匹配
Matcher.replaceAll(String s)
用 s 替换匹配到的 (regex) 内容
五、在String类的方法中使用正则表达式
更为便捷的是,String类中就有和正则表达式相关的方法
String.replaceAll(String regex,String s)
在String类replaceAll方法中,已经为我们创建好了匹配器,然后返回Matcher.replaceAll方法
String类中的replaceAll源码:
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}(Pattern.compile(String regex).matcher(String content)为Matcher类的匿名对象)
String.matches(String regex)
Pattern.matches方法前面在介绍正则元字符时使用过。
String.matches 和 Pattern.matches 都是间接的使用Matcher类的matches方法Pattern类中的matches方法源码:
public static boolean matches(String regex, CharSequence input) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
return m.matches();
}
String类中的matches方法源码:
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
String.split(String regex)
使用正则表达式对字符串进行分割,例如:
content = "12-34-56+78#9";
String[] result = content.split("[-+#]");
for(String s:result){
System.out.println(s);
}
/* 输出结果 */
12
34
56
78
9
六、最后:常用的正则表达式
校验数字的正则表达式
- 数字:“^[0-9]$”;
- n位的数字:“^\\d{n}$”;
- 至少n位的数字:“^\\d{n,}$”;
- n-m位的数字:“^\\d{n}$”;
- 0和非0的数字:“^(0|[1-9]\\d*)$”;
校验字符的正则表达式
- 汉字:^[\\u4e00-\\u9fa5]{0,}$;
- 英文和数字:[A-Za-z0-9]+^$;
- 长度为3-20的所有字符:.{3,20}^$;
- 由26个英文字母组成的字符串:^[A-Za-z]$;
- 由数字、字母、下划线组成的字符串:^\\w+$;
- 中文、英文、数字但不包含下划线等符号:^[\\u4e00-\\u9fa5A-Za-z0-9]$;
- 可以输入含有 ^%&‘,;=?\ 等字符:^[^%&’,;=?\\\\]+$;
- 禁止输入含有~的字符:^[^~]+$;
特殊需求的正则表达式
- Emai1地址: \\w+([-+.]w+)*@\\w+([-.]\w+)*.\\w+([-.]\\w+)*$
- 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
- InternetURL:[a-zA-z]+:/[^\\s]*或^https:/([\\w-]+.)+[\\w-]+(/[\\w-./?%&=]*)?$
- 手机号码:^(13[6-9]|14[5|7]115[9|1|2|3|516|78|9]|18[81|2|3|5|6|718|9])\\d{8}$