大白话说JAVA —— 正则表达式

1 介绍

        我知道各位小伙伴刚学习正则时候一定十分抓狂,因为我就是,而且这东西并没有太大的逻辑,语法一大堆,所以一段时间忘记了,重新拾起还超级难,我真服了,可以说毫无头绪。

        所以为了让小伙伴们无论是从初学还是复习更好的学习,我准备一步步从超级简单的语法开始,一步步往深里学,一步步了解如何使用正则表达式。

        这里的示例都是用Java实现,但是正则表达式的匹配规则是通用的,所以其它语言的小伙伴也可以从这里入门学习匹配规则。

PS:推荐加个匹配网站,里面还有对正则表达式的讲解。只不过是因为英文的,可以尝试用翻译。

https://regexr.com/​regexr.com/

2 作用

        Java中,正则表达式有好几种用法,常用于以下情况:(下面的代码不用看懂,只要知道它在干啥就行)

  • 验证输入的数据是否符合某个规则,例如邮箱、电话号码、身份证号等是否符合规范。

String text = "13912345678"; // 字符串
String regex = "1[3456789]\\d{9}"; // 正则表达式匹配规则
boolean match = text.matches(regex); // 判断字符串是否符合规范
// 如果上面的text符合regex规范,则返回true
  • 查找匹配特定模式的字符串,例如在一个字符串中查找所有邮件地址

// 正则表达式匹配电子邮件地址
String text = "Hello, mmy email address is john@example.com. "; // 字符串
String emailRegex = "\\w+@\\w+\\.\\w+"; // 正则表达式匹配规则
Pattern emailPattern = Pattern.compile(emailRegex); 
Matcher emailMatcher = emailPattern.matcher(text);
// 打印所有匹配的电子邮件地址
while (emailMatcher.find()) {
      System.out.println("Email address: " + emailMatcher.group());
}
  • 替换字符串中符合特定模式的部分为指定的内容,例如将字符串中所有的手机号替换为“NEW”。

String text = "Hello, my phone number is 1234567890"; // 字符串
// 正则表达式替换手机号码为 "NEW"
String phoneRegex = "\\d{10}"; // 正则表达式匹配规则
Pattern phonePattern = Pattern.compile(phoneRegex);
Matcher phoneMatcher = phonePattern.matcher(text);
String redactedText = phoneMatcher.replaceAll("NEW"); // 替换成NEW

        上面的例子就是让我们知道,java中正则可以做什么,不用担心看不懂,后面我们会一步步来学习。

        综上所述,其实正则表达式就一个作用 —— 匹配。

        验证、查找和替换都只是在匹配成功的处理而已,比如判断输入的手机号码是否符合规范,就是用对应的正则表达式去匹配,匹配成功,就说明验证成功;再比如替换,也是先对字符串中的内容进行正则匹配,找到所有符合规范的片段,再替换。

        所以说,其实正则表达式就只提供了匹配这一个功能,其它验证、查找和替换的功能是Java自己扩展来的,所以我们只要学习正则表达式的匹配规则,就掌握了Java正则表达式的根本。掌握了这个根本,你也就掌握了其它语言中正则表达式的使用。

        OK,话不多说,让我们开始一步步学习正则表达式的匹配规则吧!

3 最简单的匹配 —— 没有语法的匹配

        OK,我们先不说其它语法,我们就从最简单的匹配开始 —— 啥语法也不用。

        现在有一个字符串 —— "E先生斗图强的一匹!",我们现在匹配字符串字符串中"强的一匹",这几个字我们该怎么写?

        很简单,就直接写"强的一匹"就行了,这样系统就会去匹配字符串中符合要求的片段。

示例代码如下:

示例一:
正则表达式:强的一匹
片段为:E先生斗图强的一匹!
结果为:强的一匹

        是不是超级简单,直接无敌!

        那你可能就问,那其他那些乱七八糟的语法都有啥用啊?而且日常匹配也没这么简单啊。

        OK,那你就开始上道了,老baby,让我们开始更高难度的匹配吧!

4 无敌万能字符 —— "."

        OK,现在假设我们要匹配的语句,我们只知道我们想要的一段话,是以"E"开头,"生"结尾,中间内容不知道,并且知道中间只有一个字,然后想要匹配这三个字匹配下来,那我们该怎么匹配?

        比如内容是“E先生真的好帅啊!”

        然后我们开始匹配以"E"开头,"生"结尾的内容。

示例如下:

示例一:
正则表达式:E.生
片段为:E先生斗图强的一匹!
结果为:E先生

        通过上述的正则表达式“E.生”,我们就可以匹配到我们想要的内容。

        是不是很简单,我们现在讲解一下这个特殊符号“.”

        有什么作用呢?

        简单来说,当我们无法确定具体头尾之间包含的字符是哪一个字的时候,可以用“.”来代替中间未知的字符。

        如果中间缺两个字,我们可以写“E..真”,就可以匹配到“E先生真”。

        是不是就像一个万能符号,哪里不明补哪里。

        那我们了解了“.”的作用,但是如果我们又有一个需求呢?如果我们根本不知道开头和结束中间的内容有多少怎么办?

        就好比,我们现在要匹配以“E”开头,"厉害"结尾的片段,但是我们完全不知道中间有多少个未知符号,如果只用“.”是根本无法完全匹配到我们想要的片段,如下例子:

        字符串是"E先生超厉害!",我们可以用"E...厉害"来匹配,

        但是如果字符串改为"E先生真的超厉害!",那"E...厉害"就没法用了,因为中间是四个未确定的字,所以根本就没有通用性。

        那为了让正则表达式有通用性,那我们该怎么办呢?

        OK,别着急,我们将引进更厉害的东西!follow me !

5 贪婪匹配 —— *

        现在我们还是举上面的例子,

示例一:
正则表达式:E.*贼酷 // 表示匹配以E开头贼酷结尾,中间有一个未知字符的片段。
片段为:我的天啊!E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。
结果为:E先生,真是屁本事没有,斗图倒是有一手,贼酷

        看到没有,我们只是加了一个字符"*",就解决了我们上面无法确定中间字符数量不确定的问题,成功匹配出我们想要的字符,是不是贼6!

        OK,那我来讲解一下"*",它的作用就如上面示例所表现得,可以控制前面字符的匹配数量,匹配0个或者无数个,能够无限往后匹配,知道获取到最长并且符合要求的那段字符串,这也就是为什么叫它贪婪的原因。

        我们再根据上面的例子重新描述一下,

示例一:
正则表达式:E.贼酷 // 表示匹配以E开头贼酷结尾,中间有一个未知字符的片段。
片段为:我的天啊!E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。
结果为:匹配失败

正则表达式:E.*贼酷 // 表示匹配以E开头贼酷结尾,中间有无数个未知字符的片段。
片段为:我的天啊!E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。
结果为:E先生,真是屁本事没有,斗图倒是有一手,贼酷

示例二:
正则表达式:E先*你好 // 表示匹配以E开头,你好结尾,中间有0或多个"先"字符的片段
片段为:hellow,E先先先你好!
结果为:E先先先你好

上面就“*”符号的作用,类似她的符号还有好几个常用的,如下,

符号

作用

示例

+

匹配前面的字符至少一次或多次。

"a+b" 可以匹配 "ab"、"aab"、"aaaaab" 等字符串,但不能匹配 "b"。

*

匹配前面的字符零次或多次。

"a*"可以匹配"ab"或者"b",之所以可以匹配b,因为就算没有匹配到也算,匹配成功。

        各位小伙伴们,可以自己去探索一下,具体使用。

        OK,又掌握了一个重要工具,又距离成功迈向一大步,让我们继续探索之路吧!

6 非贪婪匹配 —— "?"

        我们可以看到上面的"*"和表格里面的"+"都是可以匹配后面的多个字符,那如果我们只想尽可能少的匹配,或者说只想匹配到符合要求的第一个呢?

        这个时候我们就可以用问号来限制匹配数量。

        我们举个例子说明一下,我们现在只想匹配所有书括号里面的信息。

示例一:
正则表达式:《.*》
片段为:《杀死一只知更鸟》,《传习录》,《西游记》这几本书都挺好看的。
匹配结果:《杀死一只知更鸟》,《传习录》,《西游记》

示例二:
正则表达式:《.*?》
片段为:《杀死一只知更鸟》,《传习录》,《西游记》这几本书都挺好看的。
匹配结果:《杀死一只知更鸟》

示例三:
正则表达式:《.*?》
片段为:《杀死一只知更鸟》,《传习录》,《西游记》这几本书都挺好看的。
全局匹配结果:
    结果0:《杀死一只知更鸟》
    结果1:《传习录》
    结果2:《西游记》

        我们可以看到,示例一中没有加"?",所以会默认开启贪婪模式,比如当遇到"《杀死一只知更鸟》"这个到时候,其实就已经符合正则表达式的要求了,但是系统会继续往后尝试,会获取符合要求,最长的字符串,这个时候就会一直匹配到最后一个书括号,也就是有了"《杀死一只知更鸟》,《传习录》,《西游记》"这样一个最长并且符合要求的字符串了。

        但是如果加了"?",我们可以看到他会跟贪婪模式相反,也就是会去获取符合要求的,并且最短的字符串,所以获取到"《杀死一只知更鸟》"就结束了。

        如果开启全局匹配,也就是匹配多次,那就可以把所有的书名都作为单独的结果匹配下来。

        这就是非贪婪模式的作用和实例。

        但是你有没有发现上面的一个弊端,就是要么全都要,要么只要最短,难道我就不能控制匹配的数量?

7 量词符——"{}"

        如果我们像控制匹配数量的话,我们可以通过量词符大括号来实现,它的语法如下,

{n}     指定需要匹配的次数
{n,}    指定至少需要匹配的次数
{n,m}   指定需要匹配的最少和最多次数。

        我们看一下示例,

示例一:
正则表达式:9{2}
片段为:19997年的天空,真美啊!
匹配结果:99

        可以看到,通过"{2}"来限制匹配9的数量为2,所以结果为2。

        我们再看看后面的用法,

示例一:
正则表达式:9{2,}
片段为:9年前,19997年的天空,真美啊!
匹配结果:999

        可以看到,通过"{2,}"来限制匹配9的至少是两个以上,所以当遇到"9年前"的9的时候,并不会匹配,因为不符合至少两个的要求,所以会跳过,匹配"19997"中的999。

        我们再看最后一个示例,

示例一:
正则表达式:9{1,2}
片段为:9年前,19997年的天空,真美啊!
匹配结果:9

        可以看到,通过"{1,2}"来限制匹配9的数量为1到2个,所以当遇到"9年前"的9的时候,也会匹配。

        这个常用于全局匹配,也就是把字符串中符合要求的全部匹配出来,如下。

示例一:
正则表达式:9{1,2}
片段为:9年前,19997年的天空,真美啊!
全局匹配结果:
    结果0:9
    结果1:99
    结果2:9

        我们可以发现,它会先把最长符合要求的匹配出来,再匹配次要符合要求的,比如999,它是先匹配到99,再匹配9。

8 定位符

        OK,前面学的东西有点多,那我们学个小菜来缓解一下,我们学一下简单的两个符号——定位符,分别是"^","$"。

符合

作用

^

匹配字符串的开头

$

匹配字符串的结尾

我们再举两个例子说明一下,

示例一:
正则表达式:^E先生 // 表示匹配以"E先生"开头的片段。
片段为:E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。
结果为:E先生

示例二:
正则表达式:^E先生 // 表示匹配以"E先生"开头的片段。
片段为:我的天啊!E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。
结果为:匹配失败
原因:因为片段不是以E先生开头的,所以无法匹配成功。

示例三:
正则表达式:想学。$ // 表示匹配以"想学。"结尾的片段。
片段为:E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。
结果为:想学。

示例四:
正则表达式:想学。$ // 表示匹配以"想学。"结尾的片段。
片段为:我的天啊!E先生,真是屁本事没有,斗图倒是有一手,贼酷,想学。。
结果为:匹配失败
原因:因为片段不是以“想学。”结尾的,所以无法匹配成功。

        OK,上面就是简单的定位符的示例,是不是理解起来不难。

        那中途休息后,我们继续学习后续的内容。

9 多才多艺的字符 —— "\"

9.1 转义

        我们上面学习了这么多符号,那好巧不巧,这些符号正好是我们想要的匹配的,那咋整?

        比如,我们要匹配"守护最好的****"这一整个完整片段,“*”是我们要匹配的字符,而不是通配符,那我们该怎么处理?

        这时候就用到我们的转义符 ——“\”。

示例一:
正则表达式:守护最好的****
片段为:守护最好的****
结果为:匹配失败,*被识别为匹配符

示例二:
正则表达式:守护最好的\*\*\*\*
片段为:守护最好的****
结果为:守护最好的****

示例二:
正则表达式:192\.168\.13\.25
片段为:192.168.13.25
结果为:192.168.13.25

        以上就是把正则表达式元字符转化为普通字符的方法。

        你以为它的作用就这个吗?

        大错特错,它还可以组合其它字符,形成特殊的元字符,可以继续整花活!

9.2 组合

​        还记得"."这个符号吗,万能匹配符,什么都可以匹配。

        它其实还有好几个小弟,而这些小弟就需要和"\"组合生效。具体如下面列表所示。

符号

作用

\d

匹配任意一个数字,可以匹配0到9

\w

匹配任意一个字母、数字或下划线,可以匹配a到z,A到Z,0到9,还有"_"这个下划线。

\s

匹配任意一个空白字符,包括空格、制表符、换行符等。

\D

匹配任何一个非数字字符

\n

匹配换行符

使用示例如下,

示例一:
正则表达式:我是大内密探007
片段为:\d\d\d
结果为:007

示例二:
正则表达式:我是tim,你是谁?
片段为:\w\w\w
结果为:tim

        通过上面几个小弟字符,我们又多了几个可以替代字符,使用起来更灵活更精确。

        但是我们可以看到上面几个替代字符有一个很大的问题,就是要么匹配0到9,要a到z,就是匹配某一个类型的所有数值,那如果我只想匹配0到5或者b到y,又或者想要匹配的字符是a,c,z之中一个,那该怎么做?

10 单字表达式——"[]"

        如何让字符的匹配范围可以按照我们的要求进行?

        这就要用到我们重要的字符——"[]"。

        方括号表示一个字符集,可以用于匹配括号内的任何一个字符。

        我们看一下示例,

示例一:
正则表达式:[6好!]
片段为:666,今天天气真的超级好!
匹配结果:6

        可以看到我们,我们的正则表达式方括号中有"6","好","!",所以只要符合这三个字符都会被匹配上。

        这就是方括号的作用,本质上方括号可以看做是一个指定范围的匹配字符。

        那如果我们想要匹配的字符是a到u呢,如果按照目前学习的字符的话,应该要如下这样写:

示例一:
正则表达式:[abcdefghijklmnopqrstu]
片段为:今天天气真的超级好,是吧,zero。
匹配结果:e

        但是这样写的话是不是太麻烦了,这么长的匹配公式,那有没有什么字符能简化这种有范围的匹配呢?

11 范围符——"-"

        通过"[]"我们可以自定义匹配字符,而如果匹配的字符是有范围的话,则可以通过范围符"-"来简化表达。

        我们来看几个示例:

示例一:
正则表达式:[12345678]
片段为:今天天气真的超级好,是吧,1239。
匹配结果:1

示例二:
正则表达式:[1-8]
片段为:今天天气真的超级好,是吧,1239。
匹配结果:1

        可以看到"[12345678]"和"[1-8]"的匹配结果是一样,也就是说如果我们的匹配字符是某一个范围的话,我们可以通过"[]"加上"-"来简化表达式。

        这两个字符的组合还可以产生很多火花,如下所示,

正则表达式

作用

讲解

[a-z]

匹配所有小写字母

其中a表示字符范围的起始字符,z表示字符范围的结束字符,中括号内的字符范围可以匹配任何一个小写字母。

[0-9]

匹配所有数字

其中0表示字符范围的起始字符,9表示字符范围的结束字符,中括号内的字符范围可以匹配任何一个数字。

[A-Za-z0-9_]

匹配任何单词字符

其中A-Za-z表示所有大小写字母,0-9表示所有数字,下划线_表示下划线字符,中括号内的字符范围可以匹配任何一个单词字符。

[\x00-\x7F]

匹配所有ASCII字符

其中\x00表示ASCII字符范围的起始字符,\x7F表示ASCII字符范围的结束字符,中括号内的字符范围可以匹配任何一个ASCII字符。

        看到上面的表达式是不是感觉通过“-”字符,表达式就变得简约了不少。

        那如果我们想要匹配除了几个字符以外的所有字符,又该怎么做?

        总不能把所有字符都列到里面去吧?

        那这里就要学习我们方括号的好搭档——"^"。

12 范围反选 —— "[^]"

        如果现在我们想要匹配除了a,b,d以外的所有字符,可以如下操作:

示例一:
正则表达式:[^E]
片段为:E哥,今天天气真的超级好!
匹配结果:哥

        上面的"[^E]",意思是匹配除了字符"E"以外的所有字符,所以第一个匹配到的就是"哥",这就是范围反选。

        也就是"[^]",匹配除了里面的所有字符。

        学到这我们已经掌握了很多了,给优秀的我们鼓个掌吧。

        我们学习了上面的内容,有没有发现一个问题,上面的字符都是匹配符合条件的单个字符,那如果我们要匹配特定的多个字符怎么办?

13 分组匹配符——"()"

        如果我们现在要匹配的是一个片段,比如要匹配"ABABABABABAB"中的AB,我们该怎么做?

这个时候,我们可以用"()"表示我们要匹配的片段,示例如下

示例一:
正则表达式:(AB)
片段为:ABABABABAB
匹配结果:AB

        通过在"()"中填写对应的字符,我们就可以匹配到符合条件的所有片段。

        但是如果不用(),我们直接用"AB"也能匹配啊,如下

示例一:
正则表达式:(AB)
片段为:ABABABABAB
匹配结果:AB

        是不是也可以,那我们要"()"干啥?

        其实它最大的作用是分组,把多个字符当做一个完整的片段使用,我举个例子

示例一:
正则表达式:(AB)*
片段为:ABABABABAB
匹配结果:ABABABABAB

示例二:
正则表达式:AB*
片段为:ABABABABAB
匹配结果:AB

        我们可以看到上面示例一,是把"AB"看做一个完整片段,然后匹配数次,就把整个字符串都匹配出来了。

        而"AB*"的含义就是,匹配以A开头,后面接多个B的字符串,但是字符串中只有单个B,所以匹配的结果为"AB"。

        这就是加"()"的作用,把括号里面的内容视作不可分割的完整片段。

        如果有啥错漏,或者还要了解的地方,可以私信我,别公开处刑,我巨脆弱玻璃心,心理承受能力-9999。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值