目录
一 快速入门
编辑
二 正则表达式基本语法
三 三个常用类
四 分组, 捕获, 反向引用
五 String类中使用正则表达式
重点看底层原理!
1.优点(可略过,主要是体验正则表达式的便捷)
//假定,编写爬虫,从百度页面得到文本
String content = "根据PC-Meter1996年的调查,平均每个互联网用户每次访问的环球网的网站有5.6个," +
"每次查看的网页20.8个,而平均阅读每一个网页所需要的时间大约1.4分钟,平均每次上网阅读环球网页的时间大约28分钟。" +
"作为这样一种具有私人和公共的双重功用的传媒,互联网效用的实现从根本上还是依赖于参与者," +
"也就是用户的增加。而这一特性又是和网络的性质是完全一致的。";
//提取所有英文单词|所有数字|字母和数字
//1.县创建一个Pattern对象
// Pattern pattern = Pattern.compile("[a-zA-Z]");
// Pattern pattern = Pattern.compile("[0-9]+");
Pattern pattern = Pattern.compile("[a-zA-Z]|[0-9]+");
//2.创建一个匹配器对象
Matcher matcher = pattern.matcher(content);
//3.循环匹配
while(matcher.find()){
System.out.println(matcher.group(0));
}
2.基本介绍
3.底层原理
package com.hspedu.regexp;
import java.util.regex.Matcher; import java.util.regex.Pattern;
/**
* @author 韩顺平 * @version 1.0
* 分析 java 的正则表达式的底层实现(重要.)
*/
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.开始匹配 /**
*
* matcher.find() 完成的任务 (考虑分组)
什么是分组,比如 (\d\d)(\d\d) ,正则表达式中有() 表示分组,第 1 个()表示第 1 组,第 2 个()表示第 2 组... 1. 根据指定的规则 ,定位满足规则的子字符串(比如(19)(98))
2. 找到后,将 子字符串的开始的索引记录到 matcher 对象的属性 int[] groups;
2.1 groups[0] = 0 , 把该子字符串的结束的索引+1 的值记录到 groups[1] = 4 2.2 记录 1 组()匹配到的字符串 groups[2] = 0 groups[3] = 2
2.3 记录 2 组()匹配到的字符串 groups[4] = 2 groups[5] = 4 2.4.如果有更多的分组.....
3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1 的值即 35, 即下次执行 find 时,就从 35 开始匹配
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]=31 和 groups[1]=35 的记录的位置,从 content 开始截取子字符串返回
就是 [31,35) 包含 31 但是不包含索引为 35 的位置 如果再次指向 find 方法.仍然安上面分析来执行
*/
while (matcher.find()) {
//小结
//1. 如果正则表达式有() 即分组
//2. 取出匹配的字符串规则如下
//3. group(0) 表示匹配到的子字符串
//4. group(1) 表示匹配到的子字符串的第一组字串
//5. group(2) 表示匹配到的子字符串的第 2 组字串
//6. ... 但是分组的数不能越界.
System.out.println("找到: " + matcher.group(0));
System.out.println("第 1 组()匹配到的值=" + matcher.group(1)); System.out.println("第 2 组()匹配到的值=" + matcher.group(2));
} }
}
1.元字符 -转义符:在检索某些特殊字符的时候,要用到转移符号,否则会报错
Note:在Java的正则表达式中,用两个\\表示其他语言中的一个\
例子:
/**
* 演示转义符
*/
public class RegExp02 {
public static void main(String[] args) {
String content = "abc$(abc(123(";
//匹配(
String regStr = "\\(";//要用\\转义,否则报错
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while(matcher.find()){
System.out.println(matcher.group(0));
}
}
}
需要用到转移符号的字符如下: . * + ( ) $ / \ ? [ ] ^ { }
2.元字符 -字符匹配符
3.元字符 -选择匹配符: 匹配某个字符串时是选择性的,比如既可以匹配这个,又可以匹配那个,则需要用到选择匹配符
| 表示或者
4. 限定符 :用于指定其前面的字符和组合项连续出现多少次
5.定位符:规定要匹配的字符串出现的位置,比如再开始还是结束位置,非常有用!
6.分组
(pattern) 非命名捕获.
捕获匹配的子字符串,编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号顺序由1开始自动编号.
(?
将匹配的子字符串捕获到一个组名称或编号名称中,用于name的字符串不能包含任何标点符号,并且不能以数字开头.可以使用单引号替代尖括号,例如(?'name').
特别分组
练习:
7.非贪婪匹配
正则匹配默认是贪婪匹配,如果在限定符后加?即变成非贪婪匹配,这时会尽可能匹配少的
8.应用实例
Pattern:正则表达式
Matcher:对输入字符串进行解释和匹配的引擎
PatternSyntaxException:表示一个正则表达式模式中的语法错误
1. Pattern类的方法matches --> 整体匹配,返回boolean
public class PatternMethod {
public static void main(String[] args) {
String content = "hello abc hello, 韩顺平教育";
String regExp = "hello.*";
boolean matches = Pattern.matches(regExp, content);
System.out.println("整体匹配=" + matches);
}
}
2. Matcher类
常用方法的演示:
public class MatcherMethod {
public static void main(String[] args) {
String content = "hello hspedu jack tom hello smith hello";
String regExp = "hello";
Pattern pattern = Pattern.compile(regExp);
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()));
}
//整体匹配,常用于校验某个字符串是否满足某个规则
System.out.println("整体匹配: " + matcher.matches());
//完成如果conteng 有hspedu 就替换成韩顺平教育
String regStr = "hspedu";
Pattern pattern1 = Pattern.compile(regStr);
Matcher matcher1 = pattern1.matcher(content);
String newStr = matcher1.replaceAll("韩顺平教育");//返回的字符串才是替换后的,原来的content不变化
System.out.println("new: " + newStr);
System.out.println("old: " + content);
}
}
案例练习一:
public class 反向引用 {
public static void main(String[] args) {
String content = "hello jack 12321-333999111 tom11111 jack22 yyy xxx";//找到12321-333999111
String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while(matcher.find()){
System.out.println("找到 " + matcher.group(0));
}
}
}
案例练习二(结巴去重):
//案例2:结巴去重
String content = "我...我要....学学学学....编程Java!";
//1.去掉所有的.
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
System.out.println("content = " + content);
//我我要学学学学编程Java!
//2.去掉重复的字
//思路
//(1)使用(.)\\1+
//(2)使用反向引用$1来替换得到的内容
// Pattern pattern1 = Pattern.compile("(.)\\1+");//分组会被记录到$1
// Matcher matcher1 = pattern1.matcher(content);
// while (matcher1.find()) {
// System.out.println("找到" + matcher1.group(0));
// }
// //使用反向引用$1来替换得到的内容
// content = matcher1.replaceAll("$1");
// System.out.println(content);
//3,使用一条语句去重
content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println(content);
1.替换
String类 public String replaceAll(String regex,String replacement)
2.判断
String类 public boolean matches(String regex)
3.分割
String类 public String[] split(String regex)