java中的正则表达式,从认识正则表达式的常用类,底层实现原理,到元字符的使用,元字符的使用细节及注意事项,分组引用及反向引用实例,Matcher类常用的其它方法,String类中的正则使用,及常用的正则表达式(摘选),
带你从0到1学会正则表达式,一篇文章足以~
Pattern 对象是一个正则表达式对象。Pattern 类没有公共构造方法。需要使用 Pattern 类 compile 方法接受一个正则表达式,获得 Pattern对象
String regex = "\\d{4}"; // 正则表达式
Pattern pattern = Pattern.compile(String regex); // 接受正则表达式(字符串),构造正则表达式对象
// 匿名对象
Pattern.compile(String regex);
在有了正则表达式对象之后,我们需要匹配器,即 Matcher 类的对象
Matcher 对象是对输入字符串解释和匹配的引擎。Matcher 类没有公共构造方法。需要使用 Pattern 对象的 matcher 方法接受匹配的内容,获得 Matcher 对象
Matcher matcher = pattern.matcher(String content);
// 匿名对象
Pattern.compile(String regex).matcher(String content);
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
下边介绍匹配器对字符串的检索时的 find() 方法和 group() 方法的底层实现
Matcher类中有两个属性: int[] groups、int oldlast
int[] gorups 在执行 find() 方法时用来存放匹配到的字符串的 开始和结束后一位下标。并在需要时通过调用 gorup() 方法来获取下标对字符串进行截取。
int oldlast 存放匹配字符结束后一位的下标,作为下一次匹配的开始位置
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()循环
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
. (小数点) 则表示“\n”和"\r"之外的任何单个字符
… :则表示任意四个字符
Pattrn.matches("....","1234");
Pattrn.matches("....","abcd");
// true
Pattrn.matches("....","12345");
// false
\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: 则表示一个长度为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 表示一个字母(大小写均可)、数字,或下划线。
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 相反,表示这个位置的字符既不是字母、数字,也不是下划线。
也就是:特殊符号(除下划线),或者空格等满足。
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
[ ] 表示匹配其中任意一个字符。
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] 相反,表示不与中括号里的任意字符匹配
a[^bcd]e:则表示a和e的中间除b,c,d这三个字符外,其他的字符都满足。
Pattrn.matches("a[\^abc]e","a1e"); // true
Pattrn.matches("a[\^abc]e","aae"); // false
[值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
与 [值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
| (竖线) 则表示或的关系,表示检测的字符串须满足其中一个时,才符合条件
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
表示匹配前面的子表达式任意次。
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 次。
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。
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
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
非命名捕获,编号为 0 的第一个捕获是由整个子字符串,其它捕获结果根据 左括号 的顺序从1自动编号
如 (\\d\d)(\\d\\d) 去匹配 “abcd1234”
命名捕获,即在分组的同给其命名,命名后分组可以通过gruop(name)来获取
如(?<分组1>\\d\d)(?<分组2>\\d\\d) 去匹配 “abcd1234”
(内部引用)对已分组的正则表达式进行引用,(n表示一个数字)如:\\1表示第一个括号里的内容,以此类推
如正则表达式 (\\d)\\1(\\d)\\2 表示的是 第一个数字和第二个数字相同,第三个数字和第四个数字相同
(外部引用),与内部引用使用方法类似,只不过是在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"; */
^ 表示以指定的字符开头
如 ^\\d{3}+[a-z]* 去匹配:
与 ^ 类似,$ 则是以指定字符结尾
如 \\d{3}+[a-z]*$ 去匹配
通常,我们在使用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?
1.连字符只能用在【】内即: 【A-Z】,在【】外面或如【a-】、【\\w-】中的边字符都只是一个普通字符
2.像. ? 这些字符在【】中也是一个普通字符
3.注意: java匹配默认贪婪匹配,即尽可能的多的,如:\d{3,} 当字符串中如:4456,会匹配 4456 而不是 445
4.要匹配内容中的字符 \ 时,因为 java和正则的转义,正则表达式要写为:
String regex = “\\\\” 才能将 \ 表示成一个普通字符
返回 gruops[0],即匹配到的子字符串初始字符下标
返回 gruops[1],即匹配到的子字符串末尾字符下标+1
将字符串整体与 String regex 匹配
用 s 替换匹配到的 (regex) 内容
更为便捷的是,String类中就有和正则表达式相关的方法
在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类的匿名对象)
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);
}
使用正则表达式对字符串进行分割,例如:
content = "12-34-56+78#9";
String[] result = content.split("[-+#]");
for(String s:result){
System.out.println(s);
}
/* 输出结果 */
12
34
56
78
9