regex-正则

一、com

正则表达式是一种格式化的文本,用来描述指定格式的字符串的字符串
描述字符串最好的方式就是一字不差的写出来,但这样效率太低,因此需要提取字符串的规律,那就要:

有分类和简写,比如所有的字母、数字
有转义
有重复次数,比如0个、n个
有逻辑运算符
有位置,前后
有大小写

本文从需求的角度来剖析regex

须知

  • 正则的意思是正规、规则。正则表达式的英文名是 Regular Expression,可以直译为描述某种规则的表达式,一般缩写为 regex或“regexp”。
  • 由数字、字母、特殊符号组成,大小写敏感
  • 从左到右匹配
  • 分为基本和扩展

场景

  • 校验输入是否符合规则,比如表单验证
  • 提取符合条件文本,比如爬虫

glob模式

我们都知道正则表达式非常复杂,正则表达式入门这一本书足足有好几百页这么厚。但是实际上我们一般情况往下用不到这么复杂的模式匹配,所以我们在shell命令当中常用的简化了的模式匹配规则,它比正则表达式要简单很多。
比如*可以代表一切的字符串,可以是0个也可以是任意多个字符,在普通正则中,只代表重复无数次。
[abc]表示匹配方括号当中的任何一个字符,
?表示匹配任何一个字符。
[0-9]表示匹配0-9当中任意一个数字,
两个
号表示任何中间目录,比如src/**/build,可以匹配到src/test/build,也可以匹配到src/current/build。
glob模式没有简写,\d这种都不能用。

二、抽象某一类字符:元字符的抽象符

正则的世界中,所有字符分为2类,一类是普通字符,这些只能匹配他们自身。还有就是元字符,能匹配一类字符,还能对位置、数量等进行限制,这些叫元字符。当然,元字符也能匹配他们自身,只是需要转义而已。

普通字符这个不用学,但元字符肯定是要学的:

1.元字符

类似“+”的用来描述匹配要求的字符,称之为元字符。其中,“+”这类描述匹配次数的元字符,称为限制符。

元字符描述
.句号匹配任意单个字符除了换行符。
[ ]字符类。匹配方括号内的任意单个字符。
[^ ]否定的字符种类。匹配除了方括号里的任意字符
*匹配>=0个重复的在*号之前的字符。
+匹配>=1个重复的+号前的字符。
?标记?之前的字符为可选.
{n,m}匹配num个大括号之间的字符 (n <= num <= m).
(xyz)字符集,匹配与 xyz 完全相等的字符串.
|或运算符,匹配符号前或后的字符.
\转义字符,用于匹配一些保留的字符 [ ] ( ) { } . * + ? ^ $ \
^从开始行开始匹配.
$从末端开始匹配.

2.简写字符集、快捷方式

须知:

一般在字符类和分组中用
没有字母的简写
简写字符、快捷方式取反:通过大小写
简写描述
.除换行符外的所有字符
\w匹配所有字母数字,等同于 [a-zA-Z0-9]
\W匹配所有非字母数字,即符号,等同于: [^\w]
\b匹配一个零宽单词边界,如一个字母与一个空格之间;例如,/\bno/ 匹配 “at noon” 中的 “no”,/ly\b/ 匹配 “possibly yesterday.” 中的 “ly”
\B匹配一个零宽非单词边界,如两个字母之间或两个空格之间;例如,/\Bon/ 匹配 “at noon” 中的 “on”,/ye\B/ 匹配 "possibly yesterday."中的 “ye”
\d匹配数字: [0-9]
\D匹配非数字: [^\d]
\s匹配所有空白符,包括空格、制表符、换页符、换行符和其他 Unicode 空格。等价于: [\t\n\f\r\p{Z}]
\S匹配所有非空格字符: [^\s]
\f匹配一个换页符
\n匹配一个换行符
\r匹配一个回车符
\t匹配一个制表符
\v匹配一个垂直制表符
\p匹配 CR/LF(等同于 \r\n),用来匹配 DOS 行终止符
\b单词边界

3.任意单个字符:点

.是元字符中最简单的例子。 .匹配任意单个字符但不匹配换行符,但不能在字符集中。 例如,表达式.ar匹配一个任意字符后面跟着是a和r的字符串。

“.ar” => The car parked in the garage.

4.单个字符之间为或:字符集/字符类[]

用方括号[ ]包裹的一组字符,里面的字符无序,与特征标群区分,相当于带 或|的字符集。[xxx]等价于(x|x|x)。用-表示包头包尾的范围。

“[Tt]he” => The car parked in the garage.

“ar[.]” => A garage is a good place to park a car.

{1}否定字符类

一般 ^ 表示一个字符串的开头
^23{2,}|23{2,}$ 表示位于一个字符串的开头或结尾部分 这个开头或结尾部分是23。
但它用在一个方括号的开头的时候,它表示这个字符集是否定的。 例如,表达式[^c]ar 匹配一个后面跟着ar的除了c的任意字符。
“[^c]ar” => The car parked in the garage.

{2}example

[xyz]:匹配 “x"或"y"或"z”

[^xyz]:补集,匹配除 “x” “y” "z"的其他任意字符

[a-z]:匹配从 “a” 到 “z” 的小写英文字母

[^a-n]:补集,匹配除 “a” 到 “n” 的其他字符

[A-Z]:匹配从 “A” 到 “Z” 的任意字符

[1-9]:匹配从 “1” 到 “9” 的任意数字

/[a-zA-Z0-9]/ 或者 /[a-z0-9]/i:匹配所有的字母和数字可以写成

^23{2,}|23{2,}$ 表示位于一个字符串的开头或结尾部分 这个开头或结尾部分是23。

5.多个字符之间为或:()和|,括号不仅仅指分组,还有特征标群和捕获符号的作用

“(f|c|m)at.?” => The fat cat sat on the mat.

[] 表示的是单个字符的或,那多个字符的或,用()来表达。可以用来分组,比如A出现2次,BC出现3次,可以(A{2})(BC{3}),将BC作为一个整体来表达
特征标群是一组写在 (…) 中的子模式。表示一个整体,就是一个里面的内容是1个整体,可以对这个整体进行次数、起止等描述。
例如之前说的 {} 是用来表示前面一个字符出现指定次数。但如果在 {} 前加入特征标群则表示整个标群内的字符重复 N 次。例如,表达式 (ab)* 匹配连续出现 0 或更多个 ab。

我们还可以在 () 中用或字符 | 表示或。例如,(c|g|p)ar 匹配 car 或 gar 或 par.

“(c|g|p)ar” => The car is parked in the garage.

(ab)表示ab整体而不像[ab]那样只匹配其中1个

6.转义

右斜线表示转义后面的字符或特征标群,用来表示{ } [ ] / \ + * . $ ^ | ? 这些特殊字符也是文本。
因为\本身也是元字符,所以\

三、位置(xx的前面,以yy开头)

1.锚点,边界

用来描述字符串的起止。在非[]中,^ 指定开头,$ 指定结尾。

2.环视(零宽度断言、前后预查)

{1}com

https://blog.csdn.net/weixin_46927507/article/details/114037804

一般正则都是匹配文本,但有些场景需要匹配位置,比如字母a的位置,而不是字母a本身。
既然是位置,那么肯定就有方向了,a前面叫lookahead前视,a后面叫lookbehind后视。

所以环视的套路就是,先匹配一些文本,然后用一些符号来表名这个文本的前面还是后面。

环视肯定要向不同的方向看,在正则里没有上下,所以就是向前后看。
环视让条件表达能力更强,原来是匹配xx文本,现在是在xx文本的前面,并且匹配yy文本。

前后,是谁的前后?就是符号后面的那个字符。

既然有xx文本的前面 (?=xx),那肯定就有不是xx文本的前面(?!xx)
(?<=xx)、(?<!xx)

{2}syntax

必须出现在()中
?= 向pattern的前面查看,?<= 向pattern的后面查看。有箭头的是后。符号的前面和后面是想要的

{3}总结

reqpattern
xx文本的前面并且是数字\d+(?=xx)
xx文本的后面并且是数字(?<=xx)\d+
不是xx文本的前面并且是数字\d+(?!xx)
不是xx文本的后面并且是数字(?<!xx)\d+

四、表示数量:量词

1. 重复m次

在正则表达式中 {} 是一个量词,常用来一个或一组字符可以重复出现的次数。 例如, 表达式 [0-9]{2,3} 匹配2到3位 0~9 的数字。
“[0-9]{2,3}” => The number was 9.9997 but we rounded it off to 10.0.

可以省略第二个参数。 例如,[0-9]{2,} 匹配至少两位 0~9 的数字。
“[0-9]{2,}” => The number was 9.9997 but we rounded it off to 10.0.

如果逗号也省略掉则表示重复固定的次数。 例如,[0-9]{3} 匹配3位数字
“[0-9]{3}” => The number was 9.9997 but we rounded it off to 10.0.

2.重复区间:重复m~n次

{1} * {0,}

匹配左边字符出现>= 0次的,也就是任意次

例如,表达式 a* 匹配0或更多个以a开头的字符。表达式[a-z]* 匹配一行中所有以小写字母开头的字符串。
“[a-z]*” => The car parked in the garage #21.

*字符和.字符搭配可以匹配所有的字符.*。
*和表示匹配空格的符号\s连起来用,如表达式\s*cat\s*匹配0或更多个空格开头和0或更多个空格结尾的cat字符串。

“\scat\s” => The fatcatsat on the concatenation. 注意第一个cat包括左右2边的空格

{2} + {1,}

+号匹配+号之前的字符出现 >=1 次。 例如表达式c.+t 匹配以首字母c开头以t结尾,中间跟着至少一个字符的字符串。
“c.+t” => The fat cat sat on the mat. 注意,只返回cat sat on the mat21个,cat不会单独返回。

{3} ?可选字符 {0,1}、非贪婪模式(最多匹配一次)、默认是贪婪模式

表示左边字符出现0或1次的字符串
例如,表达式 [T]?he 匹配字符串 he 和 The。

“[T]he” => The car is parked in the garage.
“[T]?he” => The car is parked in the garage. 注意是he不是the

3.没有单个量词的意思是“正好是m或n次”

X{n}|X{m}
X{m}(X{k})?其中m < n和k是的值n-m。

五、标志

用来丰富判断逻辑的标识符。
标志也叫模式修正符,因为它可以用来修改表达式的搜索结果。 这些标志可以任意的组合使用,它也是整个正则表达式的一部分。

使用时需要在标志前面分别加1个/,注意是左斜。表示前面的字符|特征标群使用这个标志。

标志描述
i忽略大小写。
g全局搜索。
m多行修饰符:锚点元字符 ^ $ 工作范围在每行的起始。

1. 忽略大小写(Case Insensitive)

修饰语 i 用于忽略大小写。
例如,表达式 /The/gi 表示在全局搜索 The,在后面的 i 将其条件修改为忽略大小写,则变成搜索 the 和 The,g 表示全局搜索。

“The” => The fat cat sat on the mat.
“/The/gi” => The fat cat sat on the mat.

2. 全局搜索(Global search)

默认只返回第一个。g用来返回全部结果,而不是第一个,
“/.(at)/” => The fat cat sat on the mat.
“/.(at)/g” => The fat cat sat on the mat.

3. 多行修饰符(Multiline)

多行修饰符 m 常用于执行一个多行匹配。

像之前介绍的 (^,$) 用于检查格式是否是在待检测字符串的开头或结尾。但我们如果想要它在每行的开头和结尾生效,我们需要用到多行修饰符 m。

例如,表达式 /at(.)?$/gm 表示小写字符 a 后跟小写字符 t ,末尾可选除换行符外任意字符。根据 m 修饰符,现在表达式匹配每行的结尾。

六、回溯引用、反向引用(在模式内部引用前面匹配到的内容,类似于变量)

(\w)(\w)(\2)(\1)

( ) 特征标群,捕获括号(提取数据时,只会提取括号内的内容),分组

搜索后的操作:提取、替换、转换大小写

八、贪婪匹配与惰性匹配(Greedy vs lazy matching)

正则表达式默认采用贪婪匹配模式,在该模式下意味着会匹配尽可能长的子串。我们可以使用 ? 将贪婪匹配模式转化为惰性匹配模式。

“/(.at)/" => The fat cat sat on the mat.
"/(.
?at)/” => The fat cat sat on the mat.

NPC:"那你看这样一道题:给你一些字符串,统计其末尾 e 的个数:
LeetCode
LeetCodeeee
LeetCodeee
"你:“看起来并不难,用(\w+)(e*)匹配,再取 group(2) 判断即可。”

Pattern pattern=Pattern.compile("(\\w+)(e*)");
Matcher matcher=pattern.matcher("LeetCode");
if(matcher.matches()) {
   String group1=matcher.group(1);
   String group2=matcher.group(2);
   System.out.println("group1 = "+group1+", length = "+group1.length());
   System.out.println("group2 = "+group2+", length = "+group2.length());
}

NPC:“你运行一下试试看。”
输出如下:

group1=LeetCode, length=8
group2=, length=0

你:"怎么会这样?我期望的结果是 group1 等于 LeetCod,group2 等于 e 才对啊!
"NPC:“这是因为 e 仍然属于 \w 能匹配的范畴,正则表达式默认会尽可能多地向后匹配,我们王国将其称之为 贪婪匹配。”
你:“贪婪匹配,听起来和贪心算法有异曲同工之妙。”
NPC:“没错,贪婪匹配和贪心算法原理是一致的。与之对应的匹配方式叫做 非贪婪匹配,非贪婪匹配 会在能匹配目标字符串的前提下,尽可能少的向后匹配。”
你:“那么,我要怎样指定匹配方式为非贪婪匹配呢?”
NPC:"也很简单,在需要非贪婪匹配的正则表达式后面加个 ? 即可表示非贪婪匹配。

Pattern pattern=Pattern.compile("(\\w+?)(e*)");
Matcher matcher=pattern.matcher("LeetCode");
if(matcher.matches()) {
   String group1=matcher.group(1);
   String group2=matcher.group(2);
   System.out.println("group1 = "+group1+", length = "+group1.length());
   System.out.println("group2 = "+group2+", length = "+group2.length());
}

运行程序,输出如下:

group1=LeetCod, length=7
group2=e, length=1

你:“这里也用的是?,我记得之前?表示的是匹配 0 次或者 1 次,两个符号不会混淆吗?”
NPC:“不会混淆的,你仔细想一想就能明白了,如果只有一个字符,那就不存在贪婪不贪婪的问题,如果匹配多次,那么表示非贪婪匹配的?前面必有一个标志匹配次数的符号。所以不会出现混淆。”
你:“最后一个问题,为什么这里没有匹配成 group1 等于 L,group2 等于 ee… 哦我明白了,如果这样匹配的话,字符串LeetCode就无法和正则表达式匹配起来。怪不得非贪婪匹配的定义是在能匹配目标字符串的前提下,尽可能少的向后匹配。”
NPC:“就是这个原理,看来你是真的完全明白了。”

九、example

1. 手机号

  • 目前国内的手机号码是1(3/4/5/7/8)开头的 11 位数字,因此手机号码的正则可以分解为以下几部分:
    以 1 开头:/^1/
    第 2 位为3、4、5、7、8中的一个:/[34578]/ 或 /(3|4|5|7|8)/
    剩余 3-11 位均为数字,并以数字结尾:/\d{9}$/

  • 组合起来即为/^1[34578]\d{9}$//^1(3|4|5|7|8)\d{9}$/,因为使用捕获(特征标群)括号存在性能损失,所以推荐使用第一种写法。

2. 电子邮件

标准的电子邮件组成为 <yourname>@<domain>.<extension><optional-extension>

  • 每部分的格式标准为(进行了相应的简化,主要为展示如何书写正则):
    yourname:任意英文字母(a-z/A-Z)、数字(0-9)、下划线(_)、英文句点(.)、连字符(-),长度大于 0
    domain:任意英文字母(a-z/A-Z)、数字(0-9)、连字符(-),长度大于 0
    extension:任意英文字母(a-z/A-Z),长度 2-8
    optional-extension:"."开头,后面跟任意英文字母(a-z/A-Z),长度 2-8,可选
  • 每部分的正则表达式为:
    yourname:/[a-z\d._-]+/
    domain:/[a-z\d-]+/
    extension: /[a-z]{2,8}/
    optional-extension:/(.[a-z]{2,8})?/
  • 组合起来形成最后的正则表达式:
    /^([a-z\d._-]+)@([a-z\d-]+)\.([a-z]{2,8})(\.[a-z]{2,8})?$/
    为了增加可读性可以将每部分用"()"包起来,并不要忘记起始和结束符 ^$。

3.中文字符 [\u4e00-\u9fa5]

十、javaAPI

正则又叫模式,pattern

1.正则的对象pattern,判断是否匹配用matches(),提取用matcher

正则生成pattern对象

  • 可以用来验证指定串是否符合。
Pattern pattern = Pattern.compile("[0-9]+");
System.out.println("123".matches("[0-9]+"));
System.out.println(pattern.matcher("123").matches());
  • 可以用来提取指定串中的内容,matcher对象一个提取括号生成一个group,可以用循环。
Pattern pattern=Pattern.compile("Name:(\\w+)\\s*Age:(\\d{1,3})");
Matcher matcher=pattern.matcher("Name:Aurora Age:18");
if(matcher.matches()) {
   String group1=matcher.group(1);
   String group2=matcher.group(2);
   System.out.println(group1);// 输出为 Aurora
   System.out.println(group2);// 输出为 18
}

2.字符串直接调用调用matches

System.out.println(“Name:Aurora Age:18”.matches(“Name:\w+\s*Age:\d{1,3}”)); // 输出为 true

3.split

用字符类描述分隔符

System.out.println(Arrays.toString("二分,回溯,递归,分治".split("[,;\\s]
+")));
System.out.println(Arrays.toString("搜索;查找;旋转;遍历".split("[,;\\s]
+")));
System.out.println(Arrays.toString("数论 图论 逻辑 概率".split("[,;\\s]
+")));

[二分, 回溯, 递归, 分治]
[搜索, 查找, 旋转, 遍历]
[数论, 图论, 逻辑, 概率]

4.replaceall

System.out.println("二分,回溯,递归,分治".replaceAll("[,;\\s]+", ";"));
System.out.println("搜索;查找;旋转;遍历".replaceAll("[,;\\s]+", ";"));
System.out.println("数论 图论 逻辑 概率".replaceAll("[,;\\s]+", ";"));

二分;回溯;递归;分治
搜索;查找;旋转;遍历
数论;图论;逻辑;概率

还可以回溯引用,$1就是匹配到的字符

System.out.println("二分,回溯,递归,分治".replaceAll("([,;\\s]+)", "---$1---"));
System.out.println("搜索;查找;旋转;遍历".replaceAll("([,;\\s]+)", "---$1---"));
System.out.println("数论 图论 逻辑 概率".replaceAll("([,;\\s]+)", "---$1---"));

二分---,---回溯---,---递归---,---分治
搜索---;---查找---;---旋转---;---遍历
数论------图论------逻辑------概率
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值