正则表达式
一、正则表达式:是对字符串执行模式匹配的技术
介绍:
1.一个正则表达式,就是用某种模式去匹配字符串的一个公式。很多人因
为它们看上去比较古怪而且复杂所以不敢去使用,不过,经过练习后,就觉得这些复杂的表达式写起来还是相当简单的,而且,一旦你弄懂它们,你就能把数小时辛苦而且易错的文本处理工作缩短在几分钟(甚至几秒钟)内完成
2.特别强调,正则表达式不是只有java才有,实际上很多编程语
言都支持正则表达式进行字符串操作!如图所示。
简单测试:对模式对象和匹配器的使用
package com.zjl.test.regexp; import org.junit.jupiter.api.Test; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegExp { @Test public void testRegexp(){ //需要用正则表达式匹配的文字 String content = "1994年10月,HotJava和Java平台为公司高层进行演示。1994年,Java 1.0a版本已经可以提供下载," + "但是Java和HotJava浏览器的第一次公开发布却是在1995年5月23日SunWorld大会上进行的。SUN公司的科学指导约" + "翰·盖吉宣告Java技术。这个发布是与网景公司的执行副总裁马克·安德森的惊人发布一起进行的,宣布网景将在其浏览" + "器中包含对Java的支持。1996年1月,升阳公司成立了Java业务集团,专门开发Java技术。"; //其中\\d表示匹配一个数字 String grammar = "(\\d\\d)(\\d\\d)"; //创建一个模式对象,即正则表达式对象 Pattern pattern = Pattern.compile(grammar); //创建一个匹配器,按照正则表达式的规则对content对象进行匹配 Matcher matcher = pattern.matcher(content); int n = 0; while (matcher.find()) { //如上对正则表达式的匹配规则中加上了() // 那么matcher.group(0)表示该规则匹配到的整体即(\d\d)(\d\d) //matcher.group(1)就是group(0)所对应的规则的第一个括号 //matcher.group(2)就是group(0)所对应的规则的第二个括号 System.out.println("结果"+n+"展示:"+matcher.group(0)); System.out.println("结果"+n+"展示:"+matcher.group(1)); System.out.println("结果"+(n++)+"展示:"+matcher.group(2)); } /** * 结果0展示:1994 * 结果0展示:19 * 结果0展示:94 */ } }
二、正则表达式语法
基本介绍:运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为6种:
1.限定符
2.选择匹配符
3.分组组合和反向引用符
4.特殊字符
5.字符匹配符
6.定位符
元字符(Metacharacter)——转义号(\\)
\\符号说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错的。
例如:用(去匹配"abc$(”,在IDEA中编译就会出错
提示:在Java的正则表达式式中,两个\\代表其他语言中的一个\
需要用到转义符号的字符有以下:( . * + ( ) $ / \ ? [ ] ^ { } )
字符匹配符
应用实例:
(1)[a-z]说明:
[a-z]表示可以匹配a-z中任意一个字符,比如[a-z]、[A-Z]去匹配 a11c8 ,[a-z]会得到结果ac
[A-Z]不会有任何结果,区分大小写;
(2). java正则表达式默认是区分字母大小写的,如何实现不区分大小写
(?I)abc表示abc都不区分大小写
a(?i)bc表示bc不区分大小写
a((?i)b)c表示只有b不区分大小写
//表示匹配时不会区分大小写
Pattern pattern = Pattern.compile(grammar, Pattern.CASE_INSENSITIVE);
[A-Z]表示可以匹配A-Z中的任意一个字符。
[0-9]表示可以匹配0-9中任意一个字符。
(3)
[^A-Z]:表示可以匹配不是a-z中的任意一个字符
[^a-z]:表示可以匹配不是A-Z中的任意一个字符
[^0-9]:表示可以匹配不是0-9中的任意一个字符
(4).[abcd]表示可以匹配abcd中的任意一个字符。
(5).[^abcd]表示可以匹配不是abcd中的任意一个字符。
当然上面的abcd你可以根据实际情况修改,以适应你的需求
(6).\\d表示可以匹配0-9的任意一个数字,相当于[0-9]。
(7).\\D表示可以匹配不是0-9中的任意一个数字,相当于[^0-9]
(8).\\w匹配任意英文字符、数字和下划线,相当于[a-zA-Z0-9_]
(9). \\W相当于[^a-zA-Z0-9]和\\w刚好相反.
(10).\\s 匹配任何空白字符(空格,制表符等)
(11).\\S 匹配任何非空白字符,和\\s刚好相反
(12)(.)点匹配出\n 之外的所有字符,如果要匹配.本身则需要使用\\。
选择匹配符:在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个,这时就需要选择匹配符号 (|)
限定符:用于指定其前面的字符和组合项连续出现多少次
java匹配是默认贪婪匹配,即尽可能匹配多的
定位符:规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置,这个也是想当有用的
分组:
特别分组:
对正则表达式的小应用:检查中文,数字长度,验证电话号码首部,验证url
package com.zjl.test.regexp; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegExp01 { public static void main(String[] args) { //待验证的内容 // String content = "你好啊1245欢迎你"; //验证邮箱内容/QQ号是否正确 // String content = "18745123453"; //匹配内容为url String content = "https://www.bilibili.com/video/BV*1fh411y7R8?p=894&spm_id_from=pageDriver"; //验证汉字[\u0391-\uffe5]表示所有的汉字 // String regStr = "^[\u0391-\uffe5]+$"; //验证邮政编码 // String regStr = "^\\d{6}$"; //检查一个QQ号是不是正常在5到10位的数字 // String regStr = "^\\d{5,10}$"; //验证电话号码是否11位并且是13,14,15,17,18 开头的 // String regStr = "^1[3|4|5|7|8]\\d{9}$"; /** * ^((http|https)://)? 字符串开始(^)匹配https://,?表示可以有一个或者没有 * ([\w-]+\.)+匹配一到多个www.bilibili.xxxx.的结构 * [\w-]+匹配多个字符数字下滑线和-(相当于此时com部分) * (\\/[\w-=.&?/%#*]*)?$表示匹配//www.bilibili.com之后/开始后的所有 * 其中*表示0或多个,在[-=.&?/%#*]里的这些符号就表示符号本身的含义 */ // String regStr = "^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-=.&?*/%#]*)?$"; String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(.)*$";//其中(.)表示匹配除了\n以外的所有字符 //模式器 Pattern pattern = Pattern.compile(regStr); //匹配器 Matcher matcher = pattern.matcher(content); while (matcher.find()) { System.out.println("找到:" + matcher.group()); } } }
二、正则表达式三个常用类
java.util.regex包主要包括以下三个类 Pattern类、Matcher 类和PatternSyntaxException
Pattern类
pattern对象是一个正则表达式对象。Pattern类没有公共构造方法。要创建一个Pattern对象,调用其公共静态方法,它返回一个 Pattern对象。该方法接受一个正则表达式作为它的第一个参数,比如:Patternr = Pattern.compile(pattern);
表示完整匹配:与matcher不同,matcher只是在内容中查找符合规范模式的字符串
而matches是模式规范必须完全匹配文本内容
patter.matches(匹配语法,匹配内容);
Matcher 类
Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern类一样,Matcher也没有公共构造方法。你需要调用Pattern对象的matcher 方法来获得一个Matcher对象
.PatternSyntaxException
PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
分组、捕获、反向引用:
1.分组
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看作是一个子表达式/一个分组。
2.捕获
把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式
3.反向引用
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用(\\)分组号,外部反向引用($)分组号
反向引用测试:
注意:对于反向引用,在模式条件的内部需要使用(\\),而在外部则需要使用($)
//反向引用就是根据分组,对分组的组号用(\\)进行引用 // (\\1:表示引用第一个分组{2}表示在引用一次之后再加上2次引用) String test = "(\\d)(\\d)\\2\\1";//模式规则:4554,7997 String test1 = "^\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}"; content = "12321-333999111"; Pattern pattern = Pattern.compile(test1); //匹配器 Matcher matcher = pattern.matcher(content); while (matcher.find()) { System.out.println("找到:" + matcher.group()); } //结果为: //找到:12321-333999111
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegExpReplace { public static void main(String[] args) { String content = "救救急丫丫哈哈哈哈在不在在?";//救急丫哈在不在? String regstr = "(.)\\1+";//模式的规则 Pattern pattern1 = Pattern.compile(regstr);//创建一个模式对象 Matcher matcher1 = pattern1.matcher(content);//创建一个匹配器 while (matcher1.find()) { System.out.println("找到:"+matcher1.group(0));//找到匹配的字符串 } //将找到的字符串替换为该字符串的第一个分组,达到去重的效果,返回替换后的字符串 String s = matcher1.replaceAll("$1"); System.out.println(s); //简便省略的方法 content = Pattern.compile(regstr).matcher(content).replaceAll("$1"); System.out.println(content); } }
三、String类中使用正则表达式
1、替换功能
2、分割功能
代码测试:
@Test public void RegExp02(){ //将JDK1.3和JDK1.4替换成JDK String content = "JDK1.3还有JDK1.4以及之后JDK1.5还是JDK1.4重要"; String regStr = "(JDK)1.(3|4)"; //使用模式器和匹配器对进行替换操作 System.out.println(Pattern.compile(regStr).matcher(content).replaceAll("$1")); //字符串的替换操作,变得相对于简单 System.out.println(content.replaceAll("(JDK)1.(3|4)", "$1")); //将字符串分离,分组,可采用正则表达式,更加的方便 String[] split = content.split("[.]"); for (String s : split) { System.out.println(s); } }
//结果:
/*JDK还有JDK以及之后JDK1.5还是JDK重要
JDK还有JDK以及之后JDK1.5还是JDK重要
JDK1
3还有JDK1
4以及之后JDK1
5还是JDK1
4重要*/
简单的去重测试,string字符串的替换和分组(用正则表达式)
package com.zjl.test.regexp; import org.junit.jupiter.api.Test; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegExpReplace { public static void main(String[] args) { String content = "救救急丫丫哈哈哈哈在不在在?";//救急丫哈在不在? String regstr = "(.)\\1+";//模式的规则 Pattern pattern1 = Pattern.compile(regstr);//创建一个模式对象 Matcher matcher1 = pattern1.matcher(content);//创建一个匹配器 while (matcher1.find()) { System.out.println("找到:"+matcher1.group(0));//找到匹配的字符串 } //将找到的字符串替换为该字符串的第一个分组,达到去重的效果,返回替换后的字符串 String s = matcher1.replaceAll("$1"); System.out.println(s); //简便省略的方法 content = Pattern.compile(regstr).matcher(content).replaceAll("$1"); System.out.println(content); } @Test public void RegExp02(){ //将JDK1.3和JDK1.4替换成JDK String content = "JDK1.3还有JDK1.4以及之后JDK1.5还是JDK1.4重要"; String regStr = "(JDK)1.(3|4)"; //使用模式器和匹配器对进行替换操作 System.out.println(Pattern.compile(regStr).matcher(content).replaceAll("$1")); //字符串的替换操作,变得相对于简单 System.out.println(content.replaceAll("(JDK)1.(3|4)", "$1")); //将字符串分离,分组,可采用正则表达式,更加的方便 String[] split = content.split("[.]"); for (String s : split) { System.out.println(s); } } @Test public void homeWork01(){ String content = "zjlq@q.com.cn"; String regStr = "[\\w]+@([\\w]+\\.)+[\\w]+"; Pattern pattern = Pattern.compile(regStr); Matcher matcher = pattern.matcher(content); if (content.matches(regStr)) { System.out.println("匹配成功"); } else { System.out.println("匹配失败~"); } System.out.println( matcher.matches()); //经过上面的强制匹配之后,已经到达字符串的最后,所以找不到 while (matcher.find()) { System.out.println("找到:"+matcher.group()); } } @Test // 要求验证是不是整数或者小数 // 提示:这个题要考虑正数和负数 // 123 -345 34.89 -87.9 -0.01 0.45等 public void homeWork02(){ String content = "-78.23"; String regStr = "^[-+]?([1-9]\\d*|0)(\\.\\d+)?$"; if (content.matches(regStr)) { System.out.println("匹配成功~"); } else { System.out.println("匹配失败~"); } } @Test //获取网页的协议,域名,端口,以及页面文件 public void homeWork03(){ String content = "Https://www.baidui.com:8080/index/admin/table.html"; String regStr = "^(\\w+)://([a-zA-Z.]+):(\\d+)[\\w/]*/([\\w.]+)$"; Pattern pattern = Pattern.compile(regStr); Matcher matcher = pattern.matcher(content); while (matcher.find()) { System.out.println(matcher.group(1)); System.out.println(matcher.group(2)); System.out.println(matcher.group(3)); System.out.println(matcher.group(4)); } } }
常用正则表达式:
一、校验数字的表达式 1 数字:^[0-9]*$ 2 n位的数字:^\d{n}$ 3 至少n位的数字:^\d{n,}$ 4 m-n位的数字:^\d{m,n}$ 5 零和非零开头的数字:^(0|[1-9][0-9]*)$ 6 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$ 7 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$ 8 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$ 9 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$ 10 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$ 11 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$ 12 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$ 13 非负整数:^\d+$ 或 ^[1-9]\d*|0$ 14 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$ 15 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ 16 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$ 17 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 18 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$ 19 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$ 二、校验字符的表达式 1 汉字:^[\u4e00-\u9fa5]{0,}$ 2 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$ 3 长度为3-20的所有字符:^.{3,20}$ 4 由26个英文字母组成的字符串:^[A-Za-z]+$ 5 由26个大写英文字母组成的字符串:^[A-Z]+$ 6 由26个小写英文字母组成的字符串:^[a-z]+$ 7 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$ 8 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$ 9 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$ 10 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ 11 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+ 12 禁止输入含有~的字符:[^~\x22]+ 三、特殊需求表达式 1 Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 2 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.? 3 InternetURL:[a-zA-z]+://[^\s]* 或 ^https://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ 4 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ 5 电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$ 6 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7} 7 身份证号: 15或18位身份证:^\d{15}|\d{18}$ 15位身份证:^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$ 18位身份证:^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{4}$ 8 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$ 9 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 10 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$ 11 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$ 12 日期格式:^\d{4}-\d{1,2}-\d{1,2} 13 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$ 14 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$ 15 钱的输入格式: 16 1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$ 17 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$ 18 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$ 19 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$ 20 5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$ 21 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$ 22 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$ 23 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$ 24 备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里 25 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$ 26 中文字符的正则表达式:[\u4e00-\u9fa5] 27 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)) 28 空白行的正则表达式:\n\s*\r (可以用来删除空白行) 29 HTML标记的正则表达式:<(\S*?)[^>]*>.*?|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力) 30 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式) 31 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始) 32 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字) 33 IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)