正则表达式基本使用

1 基本概念

正则表达式中的元素由字符元字符组成。字符又叫做普通字符即普通的计算机编码,如我们常用的英文字符、数字、中文等等;元字符又被称为特殊字符即在正则表达式中存在特殊意义的字符,如\*$等等,详情可见MDN 文档。本文主要介绍正则表达式的基本使用,以及一些常见正则问题的处理。

2 一对一匹配

正则表达式与目标字符一对一匹配。

当我们想匹配a字符,正则表达式可以直接写成/a/
p1
但是匹配的是元字符时,如$,直接写成/$/是无法匹配成功的,因为$在正则中存在特殊意义。
p2
此时我们需要使用\对元字符进行转义,即消除元字符的特殊意义。
p3
个别普通字符在\的修饰下也会拥有特殊含义,如\n(换行)、\r(回车)等等。

小结:匹配普通字符:/普通字符/;匹配元字符:/\元字符/

正则表达式描述&举例关联
/普通字符/匹配普通字符-
/\元字符/匹配元字符-
/\n/匹配换行符(LF)换行和回车
/\r/匹配回车符(CR)换行和回车
/\f/匹配换页符-
/\t/匹配制表符(Tab)-
/\v/匹配垂直制表符-

3 一对多匹配

正则表达式之所以强大,就是其能进行模糊匹配。使用集合区间通配符的方式实现一对多的匹配,实现字符模糊。涉及:[]-通配符

3.1 集合

使用[]或者[^]将字符一一列举,[]:代表匹配列举的内容;[^]:代表匹配除列举以外的内容。

假如想匹配 1 到 9 之间的任意一个奇数,[一对一匹配](#2 一对一匹配)肯定无法满足当前的要求。此时我们可以使用集合的方式来处理。

p4

p5

3.2 区间

需要匹配的字符连贯时,可以使用一个-区间来代替一一列举

当需要匹配任意大于 4 的数字时,我们可以直接写成/[56789]/,但是更简单的方法是写成/[5-9]/

  • /[a-z]/:匹配任意小写字母;
  • /[\u4e00-\u9fa5]/:匹配任意汉字;

p6

3.3 通配符

通配符用于匹配一类字符。

在开发中时常会遇到仅允许用户输入数字的场景,用区间方式可写成/[0-9]/,如果用通配符写法可以写成:\d。懒惰才是人类文明进步的真正动力呀;

p8

集合、区间和通配符可以合并使用的,当他们同时存在时会取它们的并集。

p10

小结:集合写法:/[列举字符]/;区间写法:/[开始-结束]/;通配符写法:/通配符/

正则表达式描述&举例关联
/[13579]/[]列举的全部字符-
/[5-9]/[]内区间的全部字符-
/\d/匹配全部数字等价于[0-9]
/\D/匹配除数字以外的任意字符等价于 [^\d]
/\w/0 至 9、a 至 z、A 至 Z 以及_等价于 [a-zA-Z_\d]
/\W/0 至 9、a 至 z、A 至 Z 以及_以外字符等价于 [^\w]
/\s/全部空白字符,如:
空字符、\n、\r、\f、\t、\v
等价于[\n\r\f\t\v ]
/\S/全部非空白字符等价于[^\s]
/./除\n 和\r 以外的全部字符等价于[^\n\r]

4 次数限定

本节就来讲讲设置字符重复的次数,实现长度模糊。涉及:?{}*+

4.1 ?元字符

使用?修饰的字符或者分组可匹配 0 次或者 1 次。如正则:/https?/,可以同时匹配httphttps

p11

p12

4.2 {}元字符

如果想同时匹配httphttpshttpss三种字符串,按照前面说的?元字符可以写成/https?s?/,但是这样的写法显然很冗长,这时我们就可以用到{}元字符了,正则可写成/https{0,2}/{n,m}修饰的字符或者分组可匹配n-m次;{}有四种写法:

  • {count}:精确count 次;

  • {min,max}:min 次到 max 次;

  • {min,}:至少 min 次;

  • {0,max}:至多 max 次。

p13

4.3 *元字符

使用*修饰的字符或者分组可匹配0 次或者多次。等价于/https{0,}/

p14

p15

4.4 +元字符

*类似代表一个区间,使用+修饰的字符或者分组至少匹配一次,等价于/https{1,};

p16

p17

小结?:匹配 0 至 1 次;{}:匹配指定次数或者指定区间;*:匹配大于等于 0 次;+:匹配大于等于 1 次。

正则表达式描述&举例关联
/https?/出现 0 次或者 1 次
如:http、https
等价于/https{0,1}/
/https{0,2}/重复 0 次至 2 次
如:http、https、httpss
- {count}:count 次;
- {min,max}:min 次到 max 次;
- {min,}:至少 min 次 ;
- {0,max}:至多 max 次
/https*/重复大于等于 0 次
如:http、https 等
等价于/https{0,}/
/https+/重复大于等于 1 次
如:https、httpss 等
等价于/https{1,}/

5 边界限制

用于限制查询过程中的单词边界限制和句子边界限制。涉及:\b\B$^

5.1 单词边界\b

我们经常需要去判断某字符串是否存在某个单词,比如day ,如果按照往常写法写成/day/,测试后会发现含有day的单词如today也会匹配上;要想解决这个问题就需要用到单词边界的功能了。

p20

当一个区域的左右位置有且仅有一个\w([0-9a-zA-Z_])时该区域就存在单词边界,如下图所示每一个\b就是一个单词边界;

p21

此时想要匹配day但是不想匹配包含day的单词可以写出/\bday\b/

p22

5.2 非单词边界\B

单词边界以外的区域均为非单词边界,即一个区域内前后位置都为\w或者前后位置不存在\w,如下图每个_就是非单词边界。

p23

调整一下上面的例子,假如只想匹配以day结尾的单词,那么正则可以写成/\Bday\b/,当然也能写成/\w+day\b/

p24

p25

5.3 句子边界^$

现在有一段字符串:http://https.com.hellochange.cn,我们想判断它是否是以https开头以com结尾。如果不使用正则在JavaScript中可以直接使用startsWithendsWith来判断。

p26

如果使用正则来处理就必须用到^$来限制匹配的开始位置和结束位置。^表示以什么开头;$表示以什么结尾。

p27

p28

小结\b:单词边界;\B:非单词边界;^:内容以什么开头;$:内容以什么结尾。

正则表达式描述&举例关联
/\bday\b/匹配day单词-
/\Bday\b匹配以day结尾的单词
如:today
-
/^我/匹配以开头的句子
如:我是谁
-
/你$/匹配以结尾的句子
如:看见你
-
/^我.+你$/匹配以开头结尾的句子
如:我和你
-

6 修饰符

修饰符也叫标志,用于指定匹配策略。涉及:gmis

g:全局匹配(global),默认情况下正则在找到第一个匹配的内容后就会停止继续查找,如果想要找到全部符合规则的内容就可以使用全局策略。

p18

p19

m:多行匹配(multiline),以回车符和换行符做为句子分割点。

p29

p30

i:忽略大小写(insensitive),正则匹配默认是区分大小写的,如果想匹配过程中不区分大小写可添加该修饰符。

p31

p32

s.元字符可匹配回车符\r和换行符\nsingle line)。

p33

正则表达式描述&举例关联
/^https.+com$/m多行匹配以 https 开头 com 结尾的句子-
/^https.+com$/g匹配全部以 https 开头 com 结尾的句子-
/^https.+com$/i匹配以 https 开头 com 结尾的句子且不区分大小写-
/^.+$/s匹配任何字符串-

7 逻辑关系

正则中可用的逻辑关系,即「或|」和「非^」。涉及:|^

7.1 逻辑或

A|B:A或者B均满足匹配规则。

p34

7.2 逻辑非

[^AB]:匹配除A、B以外的任何字符。

p35

8 分组

()的一系列特征与使用,如分组、提取转换、回溯。

8.1 分组基础

使用()元字符将正则表达式进行分组,每一个分组都是一个子表达式,每一个子表达式是一个整体,类比为1个“字符”。

假如有一段字符串,我们想知道该字符串是否全由ABC组成,此时正则应该如何书写呢?首先我们可以把ABC类比成一个字符X,由题可知ABC至少需要出现一次所以需要使用+,因为是判断整个字符串所以需要使用到^$来限制匹配的开始位置和结束位置,此时正则可以写成/^X+$/,根据()的分组功能,最终正则可以写成/^(ABC)+$/

p36

p37

8.2 提取转换

使用()进行分组,默认情况下各分组匹配的内容会被提取,使用这一特性可以很轻松的实现内容的提取和转换,如下图可以看出,除了被匹配的内容被提取,每一个分组的内容也被提取出来了。

p38

p39

假如现在需要将YYYY-MM-DD的数据转换为YYYY年MM月DD日,有了分组提取的特性该需求就很好处理了,实现如下:

// 方式一
function fn1(str) {
	const reg = /^(\d{4})-(\d{2})-(\d{2})$/;
	const result = reg.exec(str);
	if (result) {
		return `${result[1]}${result[2]}${result[3]}`;
		// 也可以使用构造函数属性,该属性只要执行正则操作就有有值,如使用reg.test(str)、str.match(reg)等
		// return `${RegExp.$1}年${RegExp.$2}月${RegExp.$3}日`;
	}
	return str;
}

// 方式二
function fn2(str) {
	const reg = /^(\d{4})-(\d{2})-(\d{2})$/;
	return str.replace(reg, (match, year, month, day) => {
		return `${year}${month}${day}`;
		// 当然这里也可以使用前面提到的构造函数全局属性
	});
}

// 方式三
function fn2(str) {
	const reg = /^(\d{4})-(\d{2})-(\d{2})$/;
	// $1等价于RegExp.$1
	return str.replace(reg, `$1年$2月$3日`);
}

8.3 非捕获分组

分组但是不捕获分组匹配的内容,涉及:(?:)提取转换小节提到过,使用()进行分组,各分组匹配的内容也会被提取,但是有时我们并不会用到分组匹配的内容,此时我们可以使用非捕获分组(?:)

p40

8.4 回溯引用

在正则表达式中可使用\1\2\3等等表示前面第一个分组、第二个分组、第三个分组匹配的内容。

有时我们需要根据前面匹配的内容来决定后面的内容就需要用到回溯功能了。如有YYYY-MM-DDYYYY/MM/DD两种时间格式,如果想用一个正则表达式把2种时间都匹配应该如何书写?如果直接写成/^(\d{4})(-|\/)(\d{2})(-|\/)(\d{2})$/,测试后会发现YYYY-MM/DDYYYY/MM-DD的日期也会匹配。

p41

p42

如果想要控制分割符前后一致,就可以用到分组的回溯引用功能了,正则可写成:/^(\d{4})(-|\/)(\d{2})\2(\d{2})$/\2代表第二个分组匹配的内容(也等价于RegExp.$2 ),即(-|\/)匹配到的内容。

p43

p44

8.5 零宽先行断言

零宽先行断言分为零宽正向先行断言(?=pattern)零宽负向先行断言(?!pattern)

零宽正向先行断言(?=pattern)代表字符串中一个零宽度的位置,该位置后面字符串pattern匹配。举个例子:字符串为:people,正则为:/peo(?=ple)/(?=ple) 其实就是代表people之间的一个零宽的位置,为了方便理解我们假定这个位置为#,字符串就变成:peo#ple,正则则变成:/peo#/,因为#并不存在,最终得到的结果就是peo

p45

零宽负向先行断言(?!pattern)也代表字符串中一个零宽度的位置,但是与正向相反,该位置后面字符串不能pattern匹配。

p46

p47

8.6 零宽后行断言

零宽后行断言分为零宽正向后行断言(?<=pattern)零宽负向后行断言(?<!pattern)

零宽正向后行断言(?<=pattern)代表字符串中一个零宽度的位置,该位置前面字符串pattern匹配。

p48

零宽负向后行断言(?<!pattern)代表字符串中一个零宽度的位置,该位置前面字符串不能pattern匹配。

p49

p50

注意 ⚠️:零宽后行断言存在兼容性问题,在 safari 浏览器和一些老版本浏览器中无法识别,所以一般不会直接使用后行断言,而是使用[替代方案](#8.7 零宽后行断言替代方案)

8.7 零宽后行断言替代方案

  1. /(?<=A)B/转化为/(?=A(B))A\1/或者/(?=AB)A(B)/【并非全部情景都适用】

假如想匹配全部含config.后缀名的文件,如果使用零宽后行断言可以直接写成:/(?<=config)\.(\w+)$/

p51

转化为前行断言为:/(?=config(\.(\w+)$))config\1/

p52

如果我们只想获取文件的后缀名,我们可以使用分组的提取功能,当然也可以把结果中的config使用replace进行移除。

p53

  1. /(?<!A)B/转化为^(?!.*AB$).+(B)【并非全部情景都适用】

假如想匹配全部不含config.后缀名的文件,如果使用零宽后行断言可以直接写成:/(?<!config)\.(\w+)$/

p54

转化为前行断言为:/^(?!.*config\.(\w+)$).+\.(\w+)$/

p55

如果想获取到后缀名和上面一样使用分组的提取功能

p56

  1. 将字符串翻转然后使用前行断言匹配
const str = "apple and people";
const reg = /(?<=app)le/;
// 将字符串翻转
const reverseStr = str.split("").reverse().join(""); // 'elpoep dna elppa'
// 将正则由后行断言转化为前行断言
const reverseReg = /el(?=ppa)/;
// 翻转后的匹配结果
const reverseResult = reverseStr.match(reverseReg); //['el', index: 11, input: 'elpoep dna elppa', groups: undefined]
// 转化为真实结果
const result = reverseResult
	? reverseResult.map((item) => item.split("").reverse().join(""))
	: null; // ['le']

小结(?=pattern):后面是pattern(?!pattern)后面不是pattern(?<=pattern)前面是pattern(?<!partterb)前面不是pattern

9 后记

第一次认真的写一篇博客,其中肯定存在很多纰漏,各位如果有发现什么错误之处或者觉得哪块表述的不清楚或者在写作上和表述上有什么建议的请留言告诉我,Thanks♪(・ω・)ノ!

10 参考

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript正则表达式和Java正则表达式在语法上有一些差异,但是它们的基本概念和用法是相似的。下面是将JavaScript正则表达式转换为Java正则表达式的一些常见规则: 1. 语法差异: - JavaScript正则表达式使用斜杠(/)作为定界符,而Java正则表达式使用双引号(")作为定界符。 - JavaScript正则表达式中的特殊字符需要进行转义,而Java正则表达式中的特殊字符不需要转义。 2. 字符类: - JavaScript正则表达式中的字符类使用方括号([])表示,而Java正则表达式使用方括号([])或者Unicode转义(\p{...})表示。 - JavaScript正则表达式中的字符类可以使用连字符(-)表示范围,而Java正则表达式中需要使用Unicode转义(\uXXXX)表示范围。 3. 量词: - JavaScript正则表达式中的量词使用花括号({})表示,而Java正则表达式使用花括号({})或者问号(?)表示。 - JavaScript正则表达式中的贪婪量词默认是贪婪模式,而Java正则表达式中的贪婪量词需要在后面添加问号(?)来表示非贪婪模式。 4. 边界匹配: - JavaScript正则表达式中的边界匹配使用插入符号(^)和美元符号($)表示,而Java正则表达式使用\A和\Z表示。 5. 其他差异: - JavaScript正则表达式中的捕获组使用圆括号(())表示,而Java正则表达式使用圆括号(())或者方括号([])表示。 - JavaScript正则表达式中的反向引用使用反斜杠加数字(\1、\2等)表示,而Java正则表达式使用美元符号加数字($1、$2等)表示。 以上是一些常见的JavaScript正则表达式转换为Java正则表达式的规则。具体转换时,还需要根据具体的正则表达式进行适当的调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值