Java正则表达式
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java正则表达式相关的知识,希望对你有一定的参考价值。
Java正则表达式
课程链接:https://www.bilibili.com/video/BV1Eq4y1E79W?p=1
在刷题过程中,也经常见正则表达式,感觉非常方便。今天偶然看到就来学一下!
简介
用于处理类似的文本问题,就是用某种模式去匹配字符串的一个公式
正则表达式是对字符串执行模式匹配的技术
正则表达式:regular expression => RegExp
底层实现(很重要)
package study01;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//分析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的应用服务器。oDva 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()完成的任务
*
* 1.根据指定的规则,定位满足条件的子字符串(比如1998)
* 2.找到后,将子字符串开的的索引记录到 matcher对象的属性 int[] groups;
* group[0] = 0, 把该字符串的结束的索引+1的值记录到 group[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();
* }
* 可以看出,根据 group[0] = 0 和 group[1] = 4 记录的位置,从content开始截取子字符串返回
* 就是[0,4) 包含0 但是不包含索引为 4 的位置
*
* 如果再次执行find方法,仍然按照上面的分析来执行
* 即group[0] = 31, group[1] = 35
*/
/**
* matcher.find()完成的任务(考虑分组)
* 什么是分组,比如:(\\d\\d)(\\d\\d),正则表达式中有()表示分组,第一个()表示第一组,第二个()表示第二组...
* 1.根据指定的规则,定位满足条件的子字符串(比如1998)
* 2.找到后,将子字符串开的的索引记录到 matcher对象的属性 int[] groups;
* 2.1 group[0] = 0, 把该字符串的结束的索引+1的值记录到 group[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,即4,即下次执行find时,就从4这个位置开始匹配
*/
while(matcher.find()){
//小结
//1.如果正则表达式有()即分组
//取出匹配到的字符串规则如下:
//group(0)表示匹配到的整体子字符串
//group(1)表示匹配到的第一组子字符串
//group(2)表示匹配到的第二组子字符串
//但是不能越界
System.out.println("找到:" + matcher.group(0));
System.out.println("第一组匹配到的值" + matcher.group(1));
System.out.println("第二组匹配到的值" + matcher.group(2));
//System.out.println("第二组匹配到的值" + matcher.group(3)); //越界
}
}
}
输出:
找到:1998
第一组匹配到的值19
第二组匹配到的值98
找到:1999
第一组匹配到的值19
第二组匹配到的值99
找到:3443
第一组匹配到的值34
第二组匹配到的值43
找到:9889
第一组匹配到的值98
第二组匹配到的值89
正则表达式语法
●基本介绍
如果要想灵活的运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为:
1.限定符
2.选择匹配符
3.分组组合和反向引用符
4.特殊字符
5.字符匹配符
6.定位符
元字符(Metacharacter) - 转移号\\\\
\\\\符号说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错的。案例:用 $ 去匹配"abc$(" 会怎样? 用(去匹配"abc$(" 会怎样?
再次提示:
在Java的正则表达式中,两个 \\\\ 代表其他语言中的一个 \\
. 的含义是匹配所有字符,如果要匹配 . ,需要进行转移
需要用到转移符号的字符有如下: . * + ( ) $ / \\ ? [ ] ^ { }
元字符-字符匹配符
补充:
- \\\\w也可以找到下划线,\\\\W相反
- \\\\s四配任何空白字符(空格制表符等)
- \\\\S匹配任何非空白字符,和\\\\s刚好相反
- "."匹配除\\r\\n之外的所有字符,如果要匹配 “.” 本身则需要使用转义字符 \\\\
- 若要匹配包括"\\r\\n"在内的任意字符,请使用诸如"[s\\S]"之类的模式(不太懂)
补充:
\\r与\\n合起来就是回车换行的意思,回车是将光标移到当前行的行首;
换行是将光标移到当前行的下一行,但还是同一列,不会回到行首。
它们合起来可以将光标移到下一行的行首,也就是回车并换行。
但在不同的系统中它们的功能也不太相同。比如在windows里,\\r\\n表示回车换行;但在linux中\\n就代表回车换行。
这也是为什么在linux下用vim打开windows编辑的文件会发现在每一行尾都有个^M字符的原因。
解释:第二行(\\\\d)?表示有0个或者1个数字;+代表1到多个
Java正则表达式默认区分大小写,如何实现不区分大小写
(?i)abc表示abc都不区分大小写
a(?i)bc表示bc不区分大小写
a((?i)b)c表示只有b不区分大小写
Pattern pattern = Pattern.compile(regEx, Pattern.CASE_INSENSITIVE);
//不区分大小写
Pattern pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);
[^a-z] 说明:
[^a-z] 表示可以匹配不是a-z中的任意一个字符 ,比如
[^a-z] 去匹配a11c8会得到什么结果? 得到118
[^a-z] {2}又会得到什么结果呢? 表示匹配不是a-z并且连续两个,结果是11
选择匹配符
在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个,这时你需要用到选择匹配符号 |
元字符-限定符
用于指定其前面的字符和组合项连续出现多少次
注意:第三行?只作用在c字符上,如果希望同时生效要括起来
String content = "111111";
String regStr = "1{4}";
这个匹配结果只有一个1111
String regStr = "1{4, 5}";
这个输出结果是11111,java匹配时默认是贪婪匹配,即尽可能匹配多的
String regStr = "a1?"; //表示匹配a或者a1
元字符-定位符
定位符,规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置,这个也是相当有用的
String content = "123abc";
String regStr = "^[0-9]+[a-z]*";
这个匹配结果是123abc
如果String content = "a123abc";
因为开头不是数字,匹配不到
如果String content = "123abc232";
这个还是匹配到123abc
String content = "123ab-12abc";
String regStr = "^[0-9]+\\\\-[a-z]+$"; //匹配不到
String regStr = "^[0-9]+.+\\\\-.+[a-z]+$"; //匹配到全部
String content = "123ab 12ab";
String regStr = "ab\\\\b";
这个匹配到的是两个ab,空格也算分界,大写的B匹配前面的
分组
捕获分组
非捕获分组
//给分组命名,然后后面取出的时候除了用编号,还可以用组名
String regStr = "(?<g1>\\\\d\\\\d)(?<g2>\\\\d\\\\d)";
String regStr = "abcde|abcef|abctx";
//也可写成
String regStr = "abc(?:de|ef|tx)"; //这个不会捕获,所以不能取出group(1)
非贪婪匹配
?
当此字符紧随任何其他限定符(*、 +、?、{}、{n} {n,m}) 之后时,匹配模式是"非贪心的"。
“非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。
例如,在字符串"oooo"中,"o+?“只匹配单个"o”,而"o+“匹配所有"o”。
正则表达式的应用实例
- 验证字符是否都是汉字(定位符加汉字范围)
String regStr = "^[\\u0391-\\uffe5]+$";
- 邮政编码,要求是以1-9开头 的一个六位数,例如123890
String regStr = "^[1-9]\\\\d{5}$";
String regStr = "^[0-9]{6}$" ; //可行,当然不一定以1开头了
String regStr = "^[0-9]{6}" ; //可行,同上
String regStr = "^[0-9]+$"; //可行,同上
- QQ号码,1-9开头的一个5位数到10位数
String regStr = "^[1-9]\\\\d{4,9}$";
- 手机号码,以13,14,15,18开头的11位数
String regStr = "^1[3|4|5|8]\\\\d{9}$";
- 验证URL,如: htps://www.bilbili.com/video/BV1fh411y7R8?from=searchseid=1831060912083761326
注意:这里?. * 等符号写在中括号里面,其含义就是一个?. ,而不是限定符
//先确定开始部分 https:// 或者 http://
Stirng regStr = "^((http|https)://)?"
//第二步:匹配域名www.bilbili.com
Stirng regStr = "^((http|https)://)?([\\\\w-]+\\\\.)+[\\\\w-]+$"
//后面部分的匹配/video/BV1fh411y7R8?from=searchseid=1831060912083761326(可能有也可能没有)
Stirng regStr = "^((http|https)://)?([\\\\w-]+\\\\.)+[\\\\w-]+(\\\\/[\\\\w-?=&%\\.#]*)?$"
三个常用的类
java.util.regex包主要包括以下三个类Pattern类、Matcher类和PatternSyntaxException
Pattern类
pattern对象是一个正则表达式对象。 Pattern 类没有公共构造方法。要创建一个Pattern对象,其实是调用其公共静态方法,它返回一个Pattern对象。该方法接受一个正则表达式作为它的第一个参数,比如: Pattern r= Pattern.compile(pattern);
Matcher类
Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern 类一样,Matcher也没有公共构造方法。你需要调用Pattern对象的matcher方法来获得一个Matcher对象
PatternSyntaxException
PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
Pattern类的方法matches
用于整体匹配,在验证输入的字符串是否满足条件时使用
整体匹配可以去掉定位符
import java.util.regex.Pattern;
public class Hello {
public static void main(String[] args) {
String content = "I am hsp from hspedu.com.";
String pattern = ".*hspedu.*";
boolean isMatch = Pattern.matches(pattern, content);
System.out.println("是否整体匹配成功”+ isMatch); //返回true
}
}//简单追下源码PatternMethod.java(还是创建了比较器对象,调用了比较器对象的matches方法)
Matcher类的相关方法
这里原字符串不会被替换,如果想要被替换,直接用原字符串接收(下面会被替换)
分组、捕获、反向引用
例如需求:
给你一段文本,请你找出所有四个数字连在一 起的子串,并且这四个数字要满足①第1位与第4位相同②第2位与第3位相同,比如1221,5775
介绍
要解决前面的问题,我们需要了解正则表达式的几个概念:
- 分组
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看作是一个子表达式/一个分组。 - 捕获
把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式 - 反向引用
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用 \\\\分组号,外部反向引用 $分组号
案例:
1.要匹配两个连续的相同数字: (\\\\d)\\\\1
2.要匹配五个连续的相同数字: (\\\\d)\\\\1{4}
3.要匹配个位与干位相同,十位与百位相同的数5225 , 1551 (\\\\d)(\\\\d)\\\\2\\\\1
思考题:
请在字符串中检索商品编号,形式如:12321-333999111这样的号码,要求满足前面是一个五位数,然后一个 - 号,然后是一个九位数,连续的每3位要相同
\\\\d{5}-(\\\\d)\\\\1{2}(\\\\d)\\\\2{2}(\\\\d)\\\\3{2}
经典的结巴程序:
把类似: "...我...学学学..编程java!";通过正则表达式修改成"我要学编程java"
1.去掉所有的点
String content = "...我...学学学..编程java!";通过正则表达式修改成"我要学编程java";
Pattern pattern = Pattern.compile("\\\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
2.去掉重复的字
思路:使用(.)\\\\1+,使用反向引用S1替换匹配到的字符(外部)
//因为正则表达式变化了,所以需要重置一下matcher
pattern = Pattern.compile("(.)\\\\1+");
matcher = pattern.matcher(content);
content = matcher.replaceAll("$1");
3.使用一条语句
content = Pattern.compile("(.)\\\\1+").matcher(content).replaceAll("$1");
在String中使用正则表达式(常用)
替换功能
String类public String replaceAll(String regex, String replacement);
//将JKD1.3,JDK1.4替换成JDK
content = content.replaceAll("JDK1\\\\.3|JDK1\\\\.4", "JDK");
判断功能
//这个方法是整体匹配
String 类 public boolean matches(String regex){}
要求验证一个手机号,必须是以138 139开头的
content.matches("13(8|9)\\\\d{8}");
分割功能
String 类 public String[] split(Stirng regex)
String content = “hello#abc-jack12smith~北京”
要求按照 # 或者 - 或者 ~ 或者数字来分割
String[] ss = content.split("#|-|~|\\\\d+");
案例
1.验证电子邮件格式是否合法Homework01java
规定电子邮件规则为:
- 只能有一个@
- @前面是用户名,可以是a-z A-Z 0-9 -字符
- @后面是域名,并且域名只能是英文字母,比如sohu.com或者tsinghua.org.cn
- 写出对应的正则表达式,验证输入的字符串是否为满足规则
String regStr = "^[\\\\w-]+@([a-zA-z]+\\\\.)+[a-zA-z]+$"
//也可以不加定位符,因为是整体匹配,写上定位符更严谨
if(content.matches(regStr))
输出“匹配成功”
2.要求验证是不是整数或者小数
提示:这个题要考虑正数还是负数
比如:123 -345 34.89 -87.9 -0.01 0.45 等
先写出简单的正则表达式,然后逐步完善
String regStr = "^[-+]?([1-9]\\\\d*|0)(\\\\.\\\\d+)?$";
3.对一个url进行解析
http://www.sohu.com:8080/abc/index.htm
a)要求得到协议是什么? http
b)域名是什么? www. sohu.com
c)端口是什么? 8080
d)文件名是什么? index.htm
思路:就是分组分别获取
//如果有别的需求,可以改进
String regStr = "^([a-zA-Z]+)://([a-zA-Z.]+):(\\\\d+)[\\\\w-/]*/([\\\\w.]+)$";
以上是关于Java正则表达式的主要内容,如果未能解决你的问题,请参考以下文章