目录
第一个例子
正则表达式的底层实现
正则表达式语法
字符匹配符
选择匹配符
限定符
定位符
捕获分组
非命名捕获
命名捕获
非捕获分组(特别分组)
非贪婪捕获
正则表达式应用
正则表达式三个常用的类
概述
pattern
matcher
反向引用
第一个例子
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regexp {
public static void main(String[] args) {
//假设,编写了爬虫,从百度页面得到如下文本
String content = "建国初期,我们在小学教科书中崇拜那些为国家做出巨大贡献的人。来自黄继光、邱少云、雷锋和其他人的故事是必不可少的。" +
"他们要么为新中国流血,要么为新中国的早期建设做出了巨大贡献。这自然值得学习。许多英雄故事被囊括在教科书中,供孩子们study。" +
"但在此案中,一名man故意杀人,却以卑鄙的方式把自己打造成“人民英雄”,还登上了小学教科书。" +
"这个人叫刘学保。他看到了一个流传广泛的英雄故事,觉得这种受到尊重和钦佩的感觉特别好。从此他开始走向邪恶的道路,依靠预谋杀人来伪造证据。" +
"他把自己伪装成人民英雄,也受到人们的钦佩。然而,经过four year的研究和专家的考察,他的谎言最终弄巧成拙,成为了历史上的罪人,永远逃脱不掉杀人凶手的身份";
//提取文章中的单词
//1.传统方法。使用遍历方式,代码量大,效率不高。
//2.正则表达式技术
//1.先创建一个pattern模式对象,模式对象,可以理解为一个正则表达式对象
Pattern pattern = Pattern.compile("[a-zA-Z]+");
//2.创建一个匹配器对象,就是matcher匹配器按照 pattern(模式/样式),到content文本中去匹配。找到就返回true,否则false
Matcher matcher = pattern.matcher(content);
//开始循环匹配
while(matcher.find()){
//匹配内容,文本,放到m.group(0)
System.out.println("找到:"+matcher.group(0));
}
}
}
为什么要学习正则表达式?
1.处理文本的利器
2.登录界面的密码,账号,邮箱,手机号码等的验证
正则表达式的底层实现
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegTheory {
public static void main(String[] args) {
String context = "1998建国初期,我们在小学教科书中崇拜那些为国家做出巨大贡献的人。来自黄继光、邱少云、雷锋和其他人的故事是必不可少的。" +n" +
" "他们要么为新中国流血,要么为新中国的早期建设做出了巨大贡献。这自然值得学习。许多英雄故事被囊括在教科书中,供孩子们study。" +n" +
" "但在此案中,一名man故意杀人,却以卑鄙的方式把自己打造成“人民英雄”,还登上了小学教科书。" +n" +
" "这个人叫刘学保。他看到了一个流传广泛的英雄故事,觉得这种受到尊重和钦佩的感觉特别好。从此他开始走向邪恶的道路,依靠预谋杀人来伪造证据。" +n" +
" "他把自1111己伪装成人民英雄,也受到人们的钦佩。然而,经过four year的研究和专家的考察,他的谎言最终弄巧成拙,成为了历史上的罪人,永远逃脱不掉杀人凶手的身份";
//匹配规则匹配所有的四个数字
String regString = "\d\d\d\d";
//创建模式对象
Pattern pattern = Pattern.compile(regString);
//创建匹配器matcher,按照正则表达式的规则,去匹配content字符串
Matcher matcher = pattern.matcher(context);
//开始匹配
while(matcher.find()){
System.out.println(matcher.group(0));
}
}
}
matcher.find() 完成的任务(考虑分组):
什么是分组?
String regString = "(\d\d)(\d\d)";
group函数传值(0或者1或者2.............)这涉及到分组,正则表达式中有()表示分组,第一个()表示第一组,第二个()表示第二组。
1.根据指定的规则,来定位满足规则的子字符串(比如1998)
2.找到后,将子字符串的开始的索引记录到matcher对象的int[] group属性中(如果之前执行过一次,下一次就会清空该数组,可以又下面断点调试证明)
2.1 把该子字符串开始的索引记录到group[0] = 0,;把该子字符串的结束的索引+1的值记录到group[1]=4
2.2 记录1组()匹配到的字符串group[2] = 0; group[3] = 2
2.3 记录2组()匹配到的字符串group[4] =2;group[5] = 4;
2.4 如果有更多的分组。。。。,可以继续
3.同时记录oldlast的值为子字符串的结束的索引+1值为4,即下次执行find时候就从4开始匹配
group方法的源码
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();
}
在while(matcher.finder())处设置断点,根据以上原理可以看出每次都会清空group数组。
group函数传值(0或者1或者2.............)这涉及到分组,正则表达式中有()表示分组,第一个()表示第一组,第二个()表示第二组以此类推。
正则表达式语法
基本介绍:如果想要灵活使用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为:
1.限定符
2.选择匹配符
3.分组组合和反向引用符
4.特殊字符
5..字符匹配符
6.定位符
元字符-转义号:当我们使用正则表达式去检索某些特殊符号的时候,需要使用到转义符号,如果不使用则报错。需要使用转义符号的字符有 . *+()$/?[]^{}-
java中转义字符要用\
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
String context = "abc$(abc(123(";
//匹配(
String regex = "\(";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while(matcher.find()){
System.out.println(matcher.group(0));
}
}
}
字符匹配符
| 符号 | 含义 | 实例 | 解释 |
| [] | 可接收的字符列表 | [efgh] | efgh任意一个字符,[.]表示就是.本身. |
| [^] | 不可接收的字符列表 | [^abc] | 除abc之外的任意一个字符,包括数字和特殊字符 |
| - | 连字符 | A-Z | 任意单个大写字母 |
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
| . | 匹配除n以外的任何字符 | a..b | 以a开头,b结尾中间包括两个任意的字符串 | aaab,abcb |
| \d | 匹配单个数字字符 | \d{3}(\d)? | 包含三个或者四个数字的字符串 | 123,6789 |
| \D | 匹配单个非数字字符,相当于[^0-9] | \D(\d)* | 以单个非数字字符开头,后接任意个数字字符串 | a,A332 |
| \w | 匹配单个数字,大小写字母字符串,相当于[0-9a-zA-Z] | \d{3}\w{4} | 以三个数字字符开头长度为7的数字字母字符串 | 234abcd,12345Pe |
| \W | 匹配单个不为数字,大小写字母字符,相当于[^0-9a-zA-Z] | \W+\d{2} +代表一个或者多个 | 以至少以一个非数字字母字符开头,2个数字字符结尾的字符串 | #29,#?@10 |
| \s | 匹配任何空白字符(空格,制表符) | |||
| \S | 匹配任何非空白字符,和s刚好相反 | |||
| . | 匹配出n之外的所有字符,如果是要本身则\. |
字符匹配案例
可以利用(?i)不区分大小写
(?i)abc 表示abc都不区分大小写
a(?i)bc表示bc不区分大小写
a((?i)b)c只有b不区分大小写
选择匹配符
在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个,这个时候你就需要用到选择匹配符号.
| 符号 | 符号 | 示例 | 解释 |
| | | 匹配"|"之前或者之后的表达式 | ab|cd | ab或者cd |
限定符
用于指定其前面的字符和组合项连续出现多少次
java匹配是一种贪婪匹配,尽量匹配多的
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
| * | 指定字符重复0次或n次 | (abc)* | 仅包含任意个abc的字符串,等效于w* | abc,abcabcabc |
| + | 指定字符重复1次或者n次 | m+(abc)* | 至少以一个m开头,后接任意个abc的字符串 | m,mabc,mabcabc |
| ? | 指定字符重复0次或者1次 | m+abc? (没有把abc括起来,就是只作用于c) | 以至少1个m开头,后接ab或者abc的字符串 | mab,mabc,mmmab,mmabc |
| {n} | 只能输入n个字符 | [a-c]{3} | 由abcd中字母组成的任意长度为3的字符串 | abc,dbc ,adc等 |
| {n,} | 指定至少n个匹配 | [abcd]{3,} | 由abcd中字母组成的任意长度不小于3的字符串 | aab,dbc,aaabdc |
| {n,m} | 指定至少n个但不多于m个匹配(java匹配是一种贪婪匹配,尽量匹配多的) | [abcd]{3,5} | 由abcd中字母组成不小于3长度,不大于5长度的字符串 | abc,abcd |
定位符
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
| ^ | 以指定字符开始 | ^[0-9]+[A-Z]* | 至少以一个数字打头,后接任意个小写字母的字符串 | 123,66a |
| $ | 以指定字符结束 | ^[0-9]\-[a-z]+$ | 以一个数字开头后接连字符"-",并以至少一个小写字母结尾的字符串 | 1-a |
| \b | 匹配目标字符串的边界 | han\b | 这里说的字符串边界是指子串间有空格,或者目标字符串的结束位置 | hanshunpin sphan shan(加粗部分被匹配到了) |
| \B | 匹配目标字符串的非边界 | han\B | 和\b的含义刚刚好相反 | hanshunpin sphan shan(加粗部分被匹配到了) |
捕获分组
| 常用分组构造形式 | 说明 |
| (pattern) | 非命名捕获,捕获匹配的子字符串。编号为0第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号顺序从1开始自动编号。 |
| (? | 命名捕获,这种分组方式捕获的字符串既可以通过编号来获取,也可以通过组名来获取 |
非命名捕获
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
String context = "hanshunpiping s1234 nn1998ha";
//匹配(
String regex = "(\d\d)(\d\d)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while(matcher.find()){
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
}
}
命名捕获
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
String context = "hanshunpiping s1234 nn1998ha";
//匹配(
String regex = "(?\d\d)(?\d\d)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while(matcher.find()){
System.out.println(matcher.group(0));
System.out.println(matcher.group("g1"));
System.out.println(matcher.group("g2"));
//也可以通过
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
}
}
非捕获分组(特别分组)
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
String context = "hanshunpiping s1234 nn1998ha";
//匹配(
String regex = "(?\d\d)(?\d\d)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while(matcher.find()){
System.out.println(matcher.group(0));
System.out.println(matcher.group("g1"));
System.out.println(matcher.group("g2"));
//也可以通过
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
}
}
非捕获分组(特别分组)
| 常用分组构造形式 | 说明 |
| (?:pattern) | 匹配pattern但是不捕获该匹配的子表达式,即他是一个非捕获匹配。例如:industry|industries 可以写成industr(?:y|ies)。 |
| (?=pattern) | 它是一个非捕获匹配windows(?=95|98|NT|2000)匹配windows2000中的windows,但是不匹配windows3.1中的windows |
| (?!pattern) | 例如:windows(?!95|98|NT|2000)不匹配windows2000中的windows,但是匹配windows3.1中的windows |
非贪婪捕获
java正则表达式默认贪婪捕获,但是我们也可以设置成非贪婪捕获。
当?紧跟*+?{n}{n,}{n,m}之后时候就是非贪婪匹配
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
String context = "hanshunpiping s111111111111 nn1998ha";
//匹配(
String regex = "\d+?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while(matcher.find()){
System.out.println(matcher.group(0));
}
}
}
正则表达式应用
验证汉字
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
//汉字
String context = "疯狂胜多负少发射点发射点发啊啊啊啊啊啊啊啊啊啊";
String regex = "^[\u4e00-\u9fa5]+$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while(matcher.find()){
System.out.println(matcher.group(0));
}
}
}
验证邮箱
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
//邮箱
String context = "3455488858@qq.com";
String regex = "^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
if(matcher.find()){
System.out.println("YES");
}else{
System.out.println("NO");
}
}
}
验证url
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
//url
String context = "https://blog.csdn.net/cjx529377/article/details/78287552?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163357292916780271538553%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163357292916780271538553&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-78287552.pc_search_result_control_group&utm_term=%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BD%AC%E4%B9%89%E5%AD%97%E7%AC%A6&spm=1018.2226.3001.4187";
String regex = "^((http|https):\/\/)([\w]+\.)+[\w]+(\/[\w]+)*\/([\w?_~&%=.-/])*$";
Pattern pattern = Pattern.compile(regex);
System.out.println(Pattern.matches(regex,context));
}
}
正则表达式三个常用的类
概述
pattern,Matcher,patternSyntaxException
pattern对象是一个正则表达式对象。pattern类没有公共的构造方法,创建对象就调用其公共的静态方法,返回一个pattern对象 Pattern pattern = Pattern.compile(regex表达式);
matcher
Matcher对象是对输入字符串进行解释和匹配的引擎
patternSyntaxException对象是一个非强制的异常类,对正则表达式语法错误进行抛异常
pattern
matcher方法用于整体匹配,可以去掉定位符,因为之前不是整体匹配,a111要被111匹配就需要定位符,说明起始位置。现在是整体匹配就不需要。pattern的matches方法底层调用matcher的matches方法
public class Se {
public static void main(String[] args) {
//邮政编码
String context = "https://blog.csdn.net/m0_57652176?spm=1018.2226.3001.5343";
String regex = "h.*";
Pattern pattern = Pattern.compile(regex);
System.out.println(Pattern.matches(regex,context));
}
}
matcher
package dao.impl;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Se {
public static void main(String[] args) {
//url
String context = "hello edu jack tom hello smith hello";
String regex = "hello";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(context);
while (matcher.find()){
System.out.println("===================");
System.out.println(matcher.start());
System.out.println(matcher.end());
System.out.println(context.substring(matcher.start(),matcher.end()));
}
//整体匹配判断某个字符串是否满足规则
boolean matches = matcher.matches();
System.out.println(matches);
String all = matcher.replaceAll("李祖德");
System.out.println(all);
}
}
反向引用
圆括号的内容被捕获后,可以在这个括号后面使用,这就是反向引用。
匹配两个连续的相同数字:(\d)\1
匹配五个连续相同数字:(\d)\1{4}
匹配千位与个位相同,百位与十位相同.5225:(\d)(\d)\2\1
前面是五位数,然后一个-,然后九位数,连续的每三位要相同。
\d{5}-(\d)\1{2}(\d)\2{2}(\d)\3{2}



