正则表达式
正则表达式(regular expression)
快速入门
public class Regexp {
public static void main(String[] args) {
String str = "返回我ewr爱俄rte方叫我金佛trei哇56iw二分ui哦t为覅ewr覅欧56文哈i欧文覅后卫阿红i娃儿忽然覅欧文";
// getLetter(str);
System.out.println("------------");
String str2 = "<a target=\"_blank\" title=\"标题1\">";
// getLetter(str2);
System.out.println("------------");
String str3 = "192.168.23.12 192.168.255.255";
getLetter(str3);
}
//提取文章中所有的英文单词
public static void getLetter(String str) {
/*
* 传统方法,(变量方式)每一个字符去取出来来匹配ascll表,代码量大,效率不高
* 正则表达式技术
* */
//1. 先创建 Pattern对象,模式对象,可以理解为正则表达对象
//Pattern pattern = Pattern.compile("[a-zA-Z]+"); //匹配字母
// Pattern pattern = Pattern.compile("[0-9]|[a-zA-Z]+"); // 匹配数字字母
// Pattern pattern = Pattern.compile("<a target=\"_blank\" title=\"(\\S*)\""); //提取百度热搜的标题
Pattern pattern = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)"); //提取ip 地址
//2. 创建一个匹配器对象
//理解 匹配器 按照 pattern(模式/样式),到 str文本中去匹配
//找到就返回 true,否则返回false
Matcher matcher = pattern.matcher(str);
//3. 开始循环匹配
while (matcher.find()) {
//匹配到的内容,文本,放到 m.group(0)
System.out.println("找到:" + matcher.group(0));// 0 获得匹配内容
// System.out.println("找到:" + matcher.group(1));// 1 获得分组,括号内容
}
}
}
再提出几个问题
解决之道-正则表达式
- 为了结局上述问题,Java提供了正则表达式技术,专门用户处理类似文本处理问题。
- 简单的说:正则表达式是对字符串执行模式匹配的技术
- 正则表达式:regular expression =》RegExp
正则表达式基本介绍
- 一个正则表达式,就是用某种模式去匹配字符串的一个公式。很多人因为他们看上去比较古怪而且复杂所以不敢去使用,不过经过联系后,就觉得这些复杂的表达式写起来还是相当简单的,而且,一旦你弄懂他们,你就能把数小时辛苦热切易错的文笔处理工作缩短在几分钟(甚至几秒中)内完成。
- 正则表达式不只是只有 java才有,实际上很多编程语言都支持正则表达式进行字符串操作。
正则表达式底层实现(重要)
-
分析底层实现 RegTheory.java
/** * @author shkstart * 分析 Java的正则表达式的底层实现(重要) */ public class RegTheory { public static void main(String[] args) { String str = "1998年12月8日,第二代Java平台的企业版j2ee发布。1999年6月"; //目标:匹配所有四个数字 //说明 //1. /d 表示一个任意的数字 String regStr = "(\\d\\d)(\\d\\d)"; //2. 创建模式对象[即正则表达式对象] Pattern pattern = Pattern.compile(regStr); //3. 创建匹配器 //说明:创建匹配器 matcher,按照正则表达式的规则 匹配 str字符串 Matcher matcher = pattern.matcher(str); //4. 开始匹配 /* * * - \d\d\d\d * matcher.find() 完成的任务 * 1. 根据指定的规则,定位满足规则的子字符串(比如 1998) * 2. 找到后,将子字符串的开始索引记录到 matcher对象的属性 int[] groups【初始化20大小,默认-1】 * groups[0] = 0,把该子字符串的结束的索引+1的只记录到 groups[1] = 4 * 3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1的值即4,即下次执行find时,就从4开始匹配 * * 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 的记录的位置,从 str开始截取字符串返回 * 就是[0,4),就是包含0但是不包含索引为4的位置. * * 如果再次执行 find方法,仍然按上面来分析 * 1999 groups[0]=31 groups[1]=35 [31,35) oldLast=35 * * - (\d\d)(\d\d) * 什么是分组,比如(\d\d)(\d\d),正则表达式中有() 表示分组,第一个()表示第一组,第二个()表示第二组,... * groups[0]=0 groups[1]=4 * 记录第一组匹配到的字符串 groups[2]=0 groups[3]=2 =>(19) * 记录第一组匹配到的字符串 groups[4]=2 groups[5]=4 =>(98) * 如果有更多的分组。。。 * */ while (matcher.find()) { System.out.println(matcher.group(0)); } } }
- 小结
- 如果正则表达式有() 即分组
- 取出匹配的字符串规则如下
- group(0) 表示匹配到的子字符串
- group(1) 表示匹配到的子字符串的第一组字符串
- group(2) 表示匹配到的子字符串的第二组字符串
- 。。。但是分组不能越界
正则表达式语法
基本介绍
- 元字符从功能上大致分为
- 限定符
- 选择匹配符
- 分组和方向引用
- 特殊字符
- 字符匹配器
- 定位符
元字符(Metacharacter)-转义号 \\
-
\\ 符号 说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错。
-
案例:用 $ 去匹配 "abc\((" 会怎样?用 ( 去匹配 "abc\)(" 会怎样?
-
再次提示:在 Java的正则表达式中,两个 \\ 代表其他语言中的一个 \ 【java会转义】
public class Regexp2 { public static void main(String[] args) { String str = "abc$(abc(123("; //匹配 String regStr = "\\("; // 如果只写个 ( 编译报错 Matcher matcher = Pattern.compile(regStr).matcher(str); while (matcher.find()) { System.out.println(matcher.group(0)); } } }
-
需要用到转义符号的字符有以下: *** . + ( ) $ \ / ? [ ] { }**
元字符-字符匹配符
引用实例
-
[a-z] 说明
[a-z] 表示可以匹配 a-z 中任意一个字符,比如 [a-z] ,[A-Z] 去匹配 a11c8 会得到什么结果
-
Java正则表达式默认是区分字母大小写的,如何实现不区分大小写
- (?i)abc :表示abc都不区分大小写
- a(?i)abc :表示bc不区分大小写
- a((?i)b)c :表示只有b不区分大小写
- Pattern pat = Pattern.compile(regEx,Pattern.CASE.INSENSITIVE)
[A-Z] :表示可以匹配 A-Z中任意一个字符
[0-9] :表达可以匹配 0-9中任意一个字符
-
[^a-z] 说明
-
[^a-z] :表示可以匹配不是 a-z中的任意一个字符,比如
我们看看 [^a-z]去匹配 a11c8 会得到什么结果? 用 [^a-z]{2} 会得到什么结果?
[^A-Z] :表示可以匹配不是 A-Z中的任意一个字符
[^0-9] :表示可以匹配不是 0-9中的任意一个字符
-
-
[abcd] :表示可以匹配 abcd 中的任意一个字符
-
[^abcd] :表示可以匹配不是 abcd中的任意一个字符。
当然上面的abcd,你可以根据实际清空修改,以适应你的需求
-
\\d :表示可以匹配 0-9的任意一个数字,相当于 [0-9]
-
\\D :表示可以匹配不是 0-9中的任意一个数字,相当于 [^0-9]
-
\\w 匹配大小写英文字符,数字和下划线,相当于 [a-zA-Z0-9]
-
\\W :相当于[^a-z-A-Z0-9] 是 \W 刚好相反
-
\\s 匹配任何空白字符【空格,制表符等】
-
\\S :匹配任何非空白字符,和 \s刚好相反
-
. 【点】 :匹配出 \n之外的所有字符,如果要匹配,本身则需要使用 \\.
元字符-选择匹配符
元字符-限定符
-
{n} 说明:n表示出现的次数,比如 a{3},1{4},(\\d)
但是这里要注意一点,1{3} 去匹配 1111111 的话,会得到什么结果?【111 111】
-
{n,m} 说明:n表示至少出现的 n次最多m次,比如 a{3,4},1{4,5},\\d{2,5} ,我们看看1{3,4} 去匹配 1111111 的话,会得到什么结果?【1111】
- 细节:Java匹配默认贪婪匹配,尽可能匹配多的
-
+ 说明
+ 表示出现1次到任意多次,比如 a+,1+,\\d+,我们看看 1+ 去匹配 1111111 的话,会得到什么结果?【1111111 】
-
* 说明
* 表示出现 0次到任意多次,比如 a*,1*,\\d* ,我们看看 a1* 去匹配 a111的话,会得到什么结果?【a1】
-
? 说明
? 表示出现 0次到1次,比如 a?,1?,\\d,我们看看 a1? 去匹配 a1111 的话,会得到什么结果呐?【a1】匹配 a2111呐?【a】
当此字符紧随任何其他限定字符【*,+,?,{n},{n,},{n,m}】之后时,匹配模式是“非贪心的”。“非贪心的”匹配搜索到的,尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的,尽可能长的字符串。
public static void main(String[] args) { String content = "hello111111 ok"; // String regStr = "\\d{3,4}"; // 默认贪婪匹配 [//找到 = 1111] String regStr = "\\d{3,4}?"; // 取消贪婪匹配 后面添加? //[找到 = 111] [找到 = 111] Matcher matcher = Pattern.compile(regStr).matcher(content); while (matcher.find()) { System.out.println("找到 = " + matcher.group(0)); } }
元字符-定位符
- han\\b 表示匹配边界的 han【这里的边界是指:被匹配的字符串最后,也可以是空格的子字符串的后面】
分组
public static void main(String[] args) {
String str = "hanshunping s7788 nn1178mdz";
String regStr = "\\d\\d\\d\\d"; //匹配4个数组的字符串 【7788 1178】
/*
* 下面就是非命名分组
* 说明
* 1. matcher.group(0) 得到匹配的字符串
* 2. matcher.group(1) 得到匹配到的字符串的第一个分组内容
* 3. matcher.group(2) 得到匹配到的字符串的第二个分组内容
* 4. ...
* */
String regStr2 = "(\\d\\d)(\\d\\d)"; //匹配4个数组的字符串 【7788 1178】
String regStr3 = "\\d{4}";
/*
* 命名分组:既可以给分组取名
* */
String regStr4 = "(?<g1>\\d\\d)(?<g2>\\d\\d)"; //匹配4个数组的字符串
Matcher matcher = Pattern.compile(regStr4).matcher(str);
while (matcher.find()) {
System.out.println("找到"+matcher.group(0));
System.out.println("第一个分组内容"+matcher.group(1));
System.out.println("第一个分组内容[通过组名]"+matcher.group("g2"));
System.out.println("第二个分组内容"+matcher.group(2));
}
}
//非捕获分组 不能使用!!! matcher.group(1)
public static void main(String[] args) {
String content = "hello 韩顺平教育 jack韩顺平林老师 韩顺平同学hello";
//1. 找到 韩顺平教育 韩顺平老师 韩顺平同学 子字符串
// String regStr="韩顺平教育|韩顺平林老师|韩顺平同学";
// String regStr = "韩顺平(?:教育|林老师|同学)";//和上面等价,不能 matcher.group(1)会报错
//2. 找到 韩顺平 这个关键字,但是要求只是查找 韩顺平教育 韩顺平老师中包含的韩顺平
// String regStr = "韩顺平(?=教育|林老师)";
//3. 找到 韩顺平 这个关键字,但是要求只是查找 不是(韩顺平教育 韩顺平老师)中包含有的韩顺平
String regStr = "韩顺平(?!教育|林老师)"; //取反
Matcher matcher = Pattern.compile(regStr).matcher(content);
while (matcher.find()) {
System.out.println("找到 = " + matcher.group(0));
}
}
- 非捕获分组 不能使用!!! matcher.group(1)
练习
- 汉字
- 邮政编码:要求是 1-9开头的一个六位数。比如 123890
- qq号码:要求是 1-9开头的一个5位数-10位数
- 手机号码:要求必须是 13,14,15,18 开头的11位数
- url:
public static void main(String[] args) {
String content = "180000000";
String url = "https://www.bilibili.com/video/BV1Eq4y1E79W?from=search&seid=5816124276308024994&spm_id_from=333.337.0.0";
// String regStr = "^[\u0391-\uffe5]+$"; //汉字
// String regStr = "^[1-9]\\d{5}$";//邮政编码
// String regStr = "^[1-9]\\d{4,9}$";//qq号
// String regStr = "^1[3|4|5|8]\\d{9}$";//手机号
/*
* url:思路
* 1. 先确定 url的开始部分
* 2. 然后同 ([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
* 3. /video/BV1Eq4y1E79W?from=search&seid= 匹配 (\\/[\\w-?=&/%.#]*)?
* */
String regStr = "^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";//url 注意[. ? *] 表示匹配就是.本身
regExp(url, regStr);
}
public static void regExp(String content, String regStr) {
Matcher matcher = Pattern.compile(regStr).matcher(content);
if (matcher.find()) {
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
正则比表达三个常用类
Patten 模式类
-
matches 方法
public static boolean matches(String regex, CharSequence input) 用于整体匹配,在验证输入的字符串是否满足条件使用
import java.util.regex.Pattern; /** * 演示matches 方法,用于整体匹配, 在验证输入的字符串是否满足条件使用 */ public class PatternMethod { public static void main(String[] args) { String content = "hello abc hello, 韩顺平教育"; //String regStr = "hello"; String regStr = "hello.*"; boolean matches = Pattern.matches(regStr, content); System.out.println("整体匹配= " + matches); } }
Matcher 匹配器类
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Matcher 类的常用方法
*/
public class MatcherMethod {
public static void main(String[] args) {
String content = "hello edu jack hspedutom hello smith hello hspedu hspedu";
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()));
}
//整体匹配方法,常用于,去校验某个字符串是否满足某个规则
System.out.println("整体匹配=" + matcher.matches());
//完成如果content 有hspedu 替换成韩顺平教育
regStr = "hspedu";
pattern = Pattern.compile(regStr);
matcher = pattern.matcher(content);
//注意:返回的字符串才是替换后的字符串原来的content 不变化
String newContent = matcher.replaceAll("韩顺平教育");
System.out.println("newContent=" + newContent);
System.out.println("content=" + content);
}
}
PattrnSyntaxException 异常类
分组,捕获,方向引用
- 给你一段文本,请你找出所有资格数字连载一起的字串,并且这四个数字要满足第一位与第四位相同,第二位和第三位相同,比如1221,5775
public static void main(String[] args) {
String str = "hello 33333 3443 javga14 tom11 jack22 yyy xxx 12331-444555333";
// String regStr = "(\\d)\\1";
// String regStr = "(\\d)\\1{4}";
// String regStr = "(\\d)(\\d)\\2\\1";
String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
Matcher matcher = Pattern.compile(regStr).matcher(str);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
应用实例
-
把类似: "我....我要....学学学学....编程java!";通过正则表达式修改成"我要学编程java"
public static void main(String[] args) { String str = "我....我要....学学学学....编程java!"; /* * 1. 找到所有的 .替换成空 * */ Matcher matcher = Pattern.compile("\\.").matcher(str); str = matcher.replaceAll(""); System.out.println(str);//我我要学学学学编程java! /* * 2. 去掉重复的字 我我要学学学学编程java * 思路 * 1. 使用 (.)\\1+ * 2. 使用方向应用 $1,来替换匹配到的内容 * */ //注意:因为正则表达式变化,所以需要重置 matcher matcher = Pattern.compile("(.)\\1+").matcher(str); //分组的捕获内容记录到 $1 while (matcher.find()) { System.out.println(matcher.group(0));//我我 学学学学 } //2. 使用方向应用 $1,来替换匹配到的内容 str = matcher.replaceAll("$1"); System.out.println(str);//我要学编程java! //3. 使用一条语句 去掉重复的字 我我要学学学学编程java! //str = Pattern.compile("(.)\\1+").matcher(str).replaceAll("$1"); //System.out.println(str); }
String 类中使用正则表达式
替换功能
plublic String replaceAll(String regex,String replacement);
public static void main(String[] args) {
String str = "2000年5月,jdk1.3,jdk1.4和j2se1.3相继发布";
//两个都可以
// str = str.replaceAll("jdk1.3|jdk1.4", "jdk");
str = str.replaceAll("jdk1\\.3|jdk1\\.4", "jdk");
System.out.println(str);//2000年5月,jdk,jdk和j2se1.3相继发布
}
判断功能
public boolean matches(String regex);
public static void main(String[] args) {
String str = "13955555555";
boolean matches = str.matches("1(38|39)\\d{8}");
if (matches) {
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
}
分隔功能
plublic String[] split(String regex);
//要求按照 # 或者 - 或者 ~ 或者 数字 来分割
public static void main(String[] args) {
String str = "hello#abc-jack22smth~北京";
String[] split = str.split("#|-|~|\\d+");
System.out.println(Arrays.toString(split));
}
本站作业
-
验证电子邮件是否合法
- 只能有一个 @
- @前面是用户名,可以是 a-z A-Z 0-9 _ - 字符
- @后面是域名,并且域名只能是英文字母。比如 sohu.com qq.com apach.org.cn
- 写出对应的正则表达式,验证输入的字符串是否为满足规则。
public static void main(String[] args) { String content = "666666@qq.com"; String regStr = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$"; if (content.matches(regStr)) { System.out.println("匹配成功"); } else { System.out.println("匹配失败"); } }
-
验证是否是整数或者小数
提示:这个题要考虑正数和负数,比如 123 -345 34.89 -87.9 -0.01 0.45
public static void main(String[] args) { /* * 老师的思路 * 1. 先写出简单的正则表达式 * 2. 在逐步的完善【根据各种情况来完善】 * */ String content = "00.23"; //0.45 String regStr = "^[-+]?([1-9]\\d*|0)(\\.\\d+)?$"; if (content.matches(regStr)) { System.out.println("匹配成功 是小数或者正数"); } else { System.out.println("匹配失败"); } }
-
对一个 url进行解析
http://www.sohu.com:8080/abc/index/html
要求的到协议是什么?
域名是什么?
端口是什么?
思路:分组,4组,分别获取到对应的值
文件名是什么?
public static void main(String[] args) { String content = "https://www.sohu.com:8080/abc//few/few/fwe/index.jsp"; String regStr = "^([a-zA-Z]+)://([a-zA-Z.]+):(\\d+)[\\w-/]*/([\\w.]+)$"; Matcher matcher = Pattern.compile(regStr).matcher(content); if (matcher.matches()) { System.out.println(matcher.group(0)); System.out.println("协议"+matcher.group(1)); System.out.println("域名"+matcher.group(2)); System.out.println("端口号"+matcher.group(3)); System.out.println("文件"+matcher.group(4)); } }
正则表达式速查表