正则表达式
基本概念:一个正则表达式,就是用某种模式去匹配字符串的一个公式。很多人因为它们看上去比较古怪而且复杂所以不敢去使用,不过,经过练习后,就觉得这些复杂的表达式写起来还是相当简单的,而且,一旦你弄懂它们,你就能把数小时辛苦而且易错的文本处理工作缩短在几分钟(甚至几秒钟)内完成。
一、底层分析
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegTheory {
public static void main(String[] args) {
String content = "1998 年 12 月 8 日,第二代 Java 平台的企业版 J2EE 发布。1999 年 6 月,Sun 公司发布了" +
"第二代 Java 平台(简称为 Java2)的 3 个版本:J2ME(Java2 Micro Edition,Java2 平台的微型" +
"版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2 平台的" +
"标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2 平台的企业版),应" +
"用 3443 于基于 Java 的应用服务器。Java 2 平台的发布,是 Java 发展过程中最重要的一个" +
"里程碑,标志着 Java 的应用开始普及 9889 ";
//目标:匹配所有四个数字
//说明
//1. \\d 表示一个任意的数字
String regStr = "(\\d\\d)(\\d\\d)";
//2. 创建模式对象[即正则表达式对象]
Pattern pattern = Pattern.compile(regStr);
//3. 创建匹配器
//说明:创建匹配器 matcher, 按照 正则表达式的规则 去匹配 content 字符串
Matcher matcher = pattern.matcher(content);
//4.开始匹配
while (matcher.find()) {
System.out.println("第 1 组()匹配到的值=" + matcher.group(1));
System.out.println("第 2 组()匹配到的值=" + matcher.group(2));
}
}
}
matcher.find() 完成的任务 (考虑分组)
1.什么是分组,比如 (\d\d)(\d\d) ,正则表达式中有() 表示分组,第 1 个()表示第 1 组,第 2 个()表示第 2 组...2.根据指定的规则 ,定位满足规则的子字符串(比如(19)(98))
2. 找到后,将 子字符串的开始的索引记录到 matcher 对象的属性 int[] groups;
2.1 groups[0] = 0 , 把该子字符串的结束的索引+1 的值记录到 groups[1] = 4
2.2 记录 1 组()匹配到的字符串 groups[2] = 0 groups[3] = 22.3 记录 2 组()匹配到的字符串 groups[4] = 2 groups[5] = 4
2.4.如果有更多的分组..... *3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1 的值即 4, 即下次执行 find 时,就从 4 开始匹配。
match.group(0)源码分析
matcher.group(0) 分析
*
* 源码:
* public String group(int group) {
* if (first < 0)
* throw new IllegalStateException("No match found");
* if (group < 0 || group > groupCount())
* throw new IndexOutOfBoundsException("No group " + group);
* if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
* return null;
* return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
* }
* 1. 根据 groups[0]=0 和 groups[1]=4 的记录的位置,从 content 开始截取子字符串返回
* 就是 [0,4) 包含 0 但是不包含索引为 4 的位置
*
* 如果再次指向 find 方法.仍然安上面分析来执行
小结
1. 如果正则表达式有() 即分组
2. 取出匹配的字符串规则如下
3. group(0) 表示匹配到的子字符串
4. group(1) 表示匹配到的子字符串的第一组字串
5. group(2) 表示匹配到的子字符串的第 2 组字串
6.但是分组的数不能越界. System.out.println("找到: " + matcher.group(0));
二、元字符
1.字符匹配符


1.java正则表达式默认是区分字母大小写的,
(?i)abc表示abc都不区分大小写;a(?i)bc表示bc不区分大小写;a((?i)b)c表示只有b不区分大小写
2.当创建Pattern对象时,指定Pattern.CASE_INSENSITIVE,表示匹配是不区分字母大小写 Pattern pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);


转义号:\\,在java的正则表达式中,两个\\代表其他语言中的一个\。
需要用到转义字符的字符有以下:. * + ( ) $ / \ ? [ ] ^ { }
2.选择匹配符
在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个,这时需要用到选择匹配符号|

3.限定符
用于指定其前面的字符和组合项连续出现多少次


java匹配默认贪婪匹配,尽可能匹配多的。例如:String regStr="\\d{3,4}";当有四个数字时,显示四个数字,而不是只显示其中的三个。
4.定位符
定位符,规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置。

5,分组组合和反向引用符
(1)常见分组形式

非命名捕获:matcher.group(0)得到匹配的字符串
matcher.group(1)得到匹配到的字符串的第1个分组内容
matcher.group(2)得到匹配到的字符串的第2个分组内容
命名捕获:既可以通过组名也可以通过编号来获取。matcher.group("组名");
(2)特别分组:



(?=pattern)和(?!pattern)刚好是相反的效果
(3)非贪婪匹配


6.应用实例
public class RegExp10 {
public static void main(String[] args) {
//1.汉字
// String content="我爱你中国";
// String regStr="^[\u0391-\uffe5]+$";
//2.邮政编码,1-9开头的一个六位数字
// String content="123980";
// String regStr="^[1-9]\\d{5}$";
//3.QQ号码,1-9数字开头的一个(5位数-10位数)
// String content="123980";
// String regStr="^[1-9]\\d{5}$";
//4.手机号码,必须以13、15、18开头的11位数字
String content="13980666666";
String regStr="^1[3|5|8]\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
System.out.println("格式正确");
}else{
System.out.println("格式错误");
}
}
}
url验证
public class RegExp11 {
public static void main(String[] args) {
String content="https://www.bilibili.com/?spm_id_from=333.1007.0.0";
/*
1.开头为https:// |http://
2.然后通过([\w-]+\.)+[\w-]来匹配www.bilibili.com
3.?spm_id_from=333.1007.0.0匹配(\/[\w-?=&/%.#]*)?
*/ // hhtps www.bilibili. com/cn
String regStr="^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
System.out.println("格式正确");
}else{
System.out.println("格式错误");
}
}
}
三、正则表达式三个常用类
1.Pattern类
pattern 对象是一个正则表达式对象。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,调用其公共静态方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数,比如:Patternr=Pattern.compile(pattern);
Pattern类的方法matches,用来做整体匹配,常用于验证一个字符串是否满足某一个要求。方法返回boolean类型。
public class PatternMethod {
public static void main(String[] args) {
String content="hello abc hello,小趴菜";
String regStr="hello.*";
boolean matches= Pattern.matches(regStr,content);
System.out.println("整体匹配="+matches);//返回:整体匹配=true
}
}
2.Matcher类
Matcher 对象是对输入字符串进行解释和匹配的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象
方法:

public class MatcherMethod {
public static void main(String[] args) {
String content="hello world hello marry jack tom smith";
String regStr="hello";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while(matcher.find()){
System.out.println("=============");
System.out.println(matcher.start());
System.out.println(matcher.end());
System.out.println("找到:"+content.substring(matcher.start(), matcher.end()));
}
//如果content中有hello就替换成你好
regStr="hello";
pattern=Pattern.compile(regStr);
matcher=pattern.matcher(content);
//返回的字符串是替换以后的结果,原字符串不发生变化
String newContent = matcher.replaceAll("你好");
System.out.println(newContent);
System.out.println(content);
}
}
3.PatternSyntaxExption
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
四、分组、捕获、反向引用
1.分组我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看作是一个子表达式/一个分组。
2. 捕获把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式
3.反向引用圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用\\分组号,外部反向引用$分组号
(1)匹配两个连续相同的数字:(\\d)\\1
(2)匹配五个连续相同数字:(\\d)\\1{4}
(3)匹配个位与千位相同,十位与百位相同的数5225,1551 (\\d)(\\d)\\2\\1
结巴去重案例
public class RegExp13 {
public static void main(String[] args) {
String content = "我....我要....学学学学....编程java!";
//1. 去掉所有的.
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
//System.out.println("content=" + content);
//2. 去掉重复的字 我我要学学学学编程java!
// 思路
//(1) 使用 (.)\\1+,用来匹配所有重复的子字符串
//(2) 使用 反向引用$1 来替换匹配到的内容
// 注意:因为正则表达式变化,所以需要重置 matcher
pattern = Pattern.compile("(.)\\1+");//分组的捕获内容记录到$1
matcher = pattern.matcher(content);
// while (matcher.find()) {
// System.out.println("找到=" + matcher.group(0));
// }
//使用 反向引用$1 来替换匹配到的内容
//例如捕获到我我我,用我代替我我我
content = matcher.replaceAll("$1");
System.out.println("content=" + content);
//3. 使用一条语句 去掉重复的字 我我要学学学学编程java!
//content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
//System.out.println("content=" + content);
}
}
五、String类中使用正则表达式
1.替换:String类public String replaceAll(String regex,String replacement)
2.匹配:String类public boolean matches(String regex){}
3.分割:String类public String[] split(String regex)
public class StringReg {
public static void main(String[] args) {
String content="2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。"
+"2001年9月24日,J2EE1.3发布。2002年2月26日,J2SE1.4发布。" +
"自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。";
//使用正则表达式,将JDK1.3和JDK1.4替换成JDK
content = content.replaceAll("JDK1.3|JDK1.4", "JDK");
System.out.println(content);
//验证一个手机号,要求必须是138或者139开头的
content="12345678978";
if(content.matches("1(38|39)\\d{8}")){
System.out.println("验证成功");
}else{
System.out.println("验证失败");
}
//按照#或者-或者~或者数字来分割
content="hello#abc-jack~smith1marry";
String[] split = content.split("#|-|~|\\d");
for(String s:split){
System.out.println(s);
}
}
}
更多推荐
所有评论(0)