正则表达式的语法及用途

不得不说,正则表达式的可读性却是比较差,但是作为开发人员,掌握正则表达式能够高效解决某些匹配问题,因此非常有必要学习正则表达式的语法及用途,本文将以一个实例带领大家轻松记忆正则语法!

b7ff9c797ea0be593d953cd853899aea.png
懵逼正则

此文是翻译的正则表达式基础学习的文章,萌新手打翻译

0x00 正则表达式简介

作为一名程序猿,相信你可能已经在程序中看到过正则表达式,你可能会对于类似下面的字符串感到非常困惑。

/^\w+([\.-]?\w)+@\w+([\.]?\w)+(\.[a-zA-Z]{2,3})+$/

此时,你可能已经在试图揣测这串儿正则表达式的意思...

正则表达式(Regex或RegExp)在加速算法游戏中十分有用,利用正则表达式,我们能够解决很多问题。初次看到正则表达式的语法结构可能会被吓到,但非常值得你掌握并在工作中正确使用正则表达式。

0x01 什么是Regex?很重要?

Regex(正则表达式)是一种通过“匹配”方式用于帮助你从任意字符串数据中提取有效信息的对象。无论是数字、字母、标点符号或者空格,Regex可以让你检查并匹配字符串中的任意字符组合。

例如,假设你现在需要从文本中获取社会保险号码或者是电子邮箱地址。那么,你可以使用Regex来检查被检索文本中是否存在相关信息,并且你还可以利用Regex替换他们,或者用于验证另一个截取的子字符串。把Regex当作是你的搜索栏——你可以根据需求约定你的的搜索规则,然后Regex会帮您搜索所需要的信息。

0x02 正则表达式的两种创建方式

1.正则字面量:

若要创建正则字面量,你只需要使用两个 \(反斜线) 来包裹Regex Pattern。

const regexLiteral = /helloworld/;

语法:/pattern/flags

2.正则表达式构造函数:

使用正则表达式构造函数将为你生成一个正则表达式

const greeting = 'hello';

const regexConstr = new RegExp(greeting);

语法:new RegExp(pattern[, flags])

**使用经验:**如果你的正则表达式是一个不变的常量,那么使用正则字面量将会有更好的性能。但如果该正则表达式经常改变,则最好使用正则表达式构造函数来动态生成。

0x03 正则表达式的使用

test()match()replace() 是三种常用的正则使用方法,因此你应当熟练使用它们。

3.1 RegExp.prototype.test()

.test() 方法会返回一个布尔值——被检索的字符串中是否存在符合pattern的信息

const str1 = "i love regex";

const str2 = "it is cool";

const hasRegex = /regex/;

hasRegex.test(str1);
// expected output: true

hasRegex.test(str2);
// expected output: false

3.2 String.prototype.match()

可以使用 .match() 方法来代替 只返回是否匹配布尔值的.test() 方法。.match() 方法可以返回在当前字符串中所有满足匹配条件的结果数组。尽管使用 .test() 方法来判断是否存在符合条件的信息的确很方便,但有时候,我们也会试控制在整个搜索匹配的过程。

这种情况下,.match()方法就派上了用场,根据你正则规则返回符合条件的结果数组,如下是一个基本使用案例。然后,你会看到当我们同时使用标志符时,.match() 将会是一个给力的方法。

const str = "I love JavaScript";

const result = str.match(/JavaScript/);

console.log(result)
// expected output: ['JavaScript']

3.3 String.prototype.replace()

.replace() 方法会在字符串中搜索一个指定的值(或者指定的正则表达式),然后会返回一个被替换了指定值的新字符串。

const sentence = 'I love dogs more than cats.';

const regex = /dogs/;

console.log(sentence.replace(regex, 'bunnies'));
// expected output: "I love bunnies more than cats."

**注意:**当使用指定值时,字符串中如果存在多个指定的值,但仅会替换第一个匹配到的值,如果想要替换多个,那么可以指定正则表达式。

const str = "Hello World World!";

const replacement = str.replace("World", "Planet");

console.log(replacement)
// expected output: “Hello Planet World!”

0x04 中括号表达式

在中括号表达式中,你可以设置用于指定需要匹配的任意字符或字符集合。

例如,const regex=/[A-Z]/。如此,将搜索匹配字母表中所有的大写字母。

[a-z] 匹配字母表中所有小写字母

[A-Z] 匹配字母表中所有大写字母

[abcd] 在字符串中匹配小写字母a、b、c或d

[a-d] 同上,即可指定每个值,也可以使用“短横线”连接的字符集合

[a-gA-C0-7] 匹配字符串中的小写字母a~g集合,大写字母A~C集合或数字0~7集合中的任意字符

[^a-zA-Z] 匹配字符串中非英文大小写字母的字符 (在指定字符集合中,使^字符,意味着匹配指定字符集的补集)

0x05 标志

在正则表达式的反斜杠后,我们可以指定一个标志或者标志的组合。正则表达式借助标志(flags)作为如何正确检索和匹配pattern中自定义字符的标准。

const sentence = 'The Cat in the Hat is not a cat.'

const regex = /[A-Z]/;

const found = sentence.match(regex);

console.log(found);
// expected output: ['T']

“标志”是一个非必须指定的内容。没有指定标志,正则表达式会匹配第一个符合规则(pattern)返回true的字符。在如上的例子中,我们的程序会返回 ['T'],因为在句子中找到了第一个大写字母T。

  • gglobal(全局),其作用是将字符串中满足表达式的所有结果均返回。换句话说,不仅仅返回满足条件的第一项,而是返回所有存在的匹配项。参考上面的例子,现在我们把g标志添加到反斜杠的后边,就像这样,const regex = /A-Z/g,然后,上面的match()方法将返回所有满足pattern(大写字母)的字符数组。因此,先检查sentence,“The Cat in the Hat is not a cat.”,然后会返回三个大写字母的数组 ['T', 'C', 'H']

  • iinsensitive(不敏感),其作用是大小写不敏感。例如,const regex = /[TheCatInTheHat]/ig 可以同时使用global和insensitive,这个表达式将会返回上面sentence中的每一个字符在返回的数组中 ['T', 'h', 'e', 'C', 'a', 't', 'i', 'n', 't', 'h', 'e', 'H', 'a', 't', 'i', 'n', 't', 'a', 'c', 'a', 't']

  • mmulti-line(多行),其作用是配合^$标志作为匹配的起点和终点,而不是整个字符串(原作者列举的例子不太好说明区别)。我的理解是,m标志用于指定多行输入的字符串应该被当作多个行。如果使用了m标志,则会根据 ^$ 来确定字符串开始匹配和结束匹配的区间,没有的话,就默认是整个字符串(如果没有 ^$ 符号,则没必要加标志 m ),理解还是很抽象,看看下面这个例子吧。

const sentence = '123\n456\nabc\nDEF\naa1';

sentence.match(/\d$/)  // 匹配字符串末尾是数字的情况
sentence.match(/\d$/m) // 多行的情况下匹配第一个满足行末尾是数字的情况
sentence.match(/\d$/g) // 加上全局标志,返回所有末尾是数字的数组
sentence.match(/\d$/gm)// 再加上多行标志,则会比较每一行
d41fef489514761744e90988b1755f57.png
示例

另外作为补充,当需要多个字符集进行匹配的时候,还有另外三个标志可以提供帮助

const sentence = 'There are 350 dogs and 17 cats in the house.'

const regex = /\d/

const found = sentence.match(regex);

console.log(found);
// expected output: ['3']
  • d\d 匹配数字类型字符,与[0-9]相同,因此在上面例子中将返回第一个匹配的数字,返回结果为['3']

  • w\w 匹配任意的字母数字和下划线,与[0-9a-zA-Z_]相同

  • s\s 匹配空白类型字符,如果将例子中改为 const regex = /\w\s/ ,那么返回的结果就是['e'],是There空格匹配返回的结果,空白字符有\n空格\t

\d\w\s的补集是\D\W\S

  • \D匹配所有非数字(等同于[^0-9]

  • \W匹配所有非数字字母和下划线字符(等同于[^0-9a-zA-Z_]

  • \S匹配所有非空白的字符(等同于[^\s]

0x06 量词

量词(quantifiers)是正则表达式中的基本符号,具有特殊意义

  • * 前一字符的匹配数量为0或更多

  • + 前一字符的匹配数量为1或更多

  • ? 前一字符的匹配数量为0或1个;前一个字符(item,项)可有可无

  • ^ 字符串的起始符号

  • $ 字符串的结尾符号

  • . 匹配任意字符,换行符除外

  • {m, n}: m是0或一个正整数,表明了至少匹配的数目, n是一个大于或等于m的正整数,表明了至多匹配数目

接下来,通过下面这个例子来看看我们所理解的Regex。

const str = 'for__if__rof__fi'

const regex = /[a-z]+/g;

console.log(found);

显而易见,正则表达式会去匹配所有的小写字母的字符,并且使用了+符号来修饰前一个规则,以匹配所有满足前一个规则的所有字符,上面的代码将打印输出:['for', 'if', 'rof', 'fi']

再看看,如果正则表达式中没有符号+的结果会是怎样

const regex = /[a-z]/g;

那么返回值将会是:['f','o','r','i','f','r','o','f','f','i'].

0x07 灵活运用

还记得我们在文章一开始就提到的那一串难以理解的字符串吗?

/^\w+([\.-]?\w)+@\w+([\.]?\w)+(\.[a-zA-Z]{2,3})+$/

这是一个常用于邮件格式化的正则表达式,现在我们已经学会了Regex的基本用法和术语,下面我们来一步步拆分理解一下这个正则表达式

const email = 'student-id@alumni.school.edu’

const regex = /^\w+([\.-]?\w)+@\w+([\.]?\w)+(\.[a-zA-Z]{2,3})+$/

const found = regex.test(email);

console.log(found);
// expected output: true
  • 1.首先,我们一部份一部份地去理解这个表达式。在字符串的开始是一个^\w+^符号表明从字符串的开头开始匹配,然后\w会匹配所有的字母、数字和下划线,符号+表明至少有一个满足前一规则。在例子,这第一部分的规则会从email中匹配到student

  • 2.然后,第二部分的规则是([\.-]?\w)+。一对括号包裹起来作为第一个捕获组,在括号里有一个字符集,该字符集将匹配.-,而?表明前一个字符有无.-都可以,是可选的。然后是\w,只会匹配一个字母、数字或下划线字符。外边的+表明前面的组合规则在字符串中至少有一项满足。所以第二部分规则,将返回-id。如果email是有两个连接符的student — id@alumni.school.edu,那么会被认为是一个不合法的邮箱

  • 3.第三部分是@\w+,会检查有一个符号@,并且其后w+表明有一个或多个字母、数字,将会匹配例子中的@alumni

  • 4.接下来是([\.]?\w)+,这部分和第二部分的规则类似,它只会检查点号的有无,这将会匹配到.school

  • 5.下一部分是(\.[a-zA-Z]{2,3})+,是检查邮件格式的重要部分。这部分将匹配邮箱地址中的顶级域名,会匹配到域名中的后缀,例如comorgnet。首先会寻找到一个.,然后是2~3个无论大小写的英文字母。在这个例子中,将匹配到.edu

  • 最后是一个$符号,表明字符串结尾

这部分个人觉得作者说的有部分不符合规则,在第4步,该规则应该是会匹配到.school.edu

15c06a9336bb4eae589ca4bdde1f7ae7.png
运行结果

这就是Regex!现在我们已经知道如何使用正则来校验邮箱地址。此外,你可以在正则表达式中使用中括号,标志符,量词来完善我们正则中可能没考虑到的极端用例。

0x08 总结

对于开发人员来说,学会Regex的知识应该非常有用。如上所示,Regex最常用于需要安全校验的情况。当开发人员需要匹配URL或通过某些文本进行解析或提取某些信息(例如yyyy-mm-dd的日期格式)时,也可以用正则来实现该功能。正则表达式无处不在!

正则表达式看起来似乎非常难以理解,有的人并以此为不去学习Regx的理由。但没必要这样,学习正则表达式是一个难度渐增的过程,今天学习的基础知识就是一个起点

0x09 参考文章和资源

  • Regex Cheatsheet

  • Regular Expression Editor

  • Regex Crossword Puzzle

  • MDN Documentation for Regex 这个非常棒

  • RegEx Golf

原文链接:《Understanding Regex 101》

翻译备注:在某需求驱使下看到此文,虽然有点基础,但是笔者为了练习翻译,与此同时也希望能够对学习正则语法的同学有所帮助。

0xFF 补充:常用的正则语法

符号usage说明
*a*前一个元素0个或多个,贪婪
?a?前一个元素0个或1个
+a+前一个元素1个或多个
[][abc]前一个元素是a或b或c
()(abc)捕获“abc”,Capture everything enclosed
\\n转义其后字符
^[^a-z]前一个字符不是a~z中任意一个字符
\n
Newline
\t
Tab
\0
Null character
-[a-z]A character in the range a~z
\s
Any whitespace character
\S
Any non-whitespace character
\d
Any digit
\D
Any noe-digit
\w
Any word character [0-9a-zA-Z]
\W
Any none-word character
(?:..)
Match everything enclosed, but won't capture
|(a|b)Match either a or b
{}a{1},a{2,},a{3,5}Exactly 1 of a, 2 or more of a, between3 and 5 of a

最后送大家一个键盘(复制后在Console运行👀):

(_=>[..."`1234567890-=~~QWERTYUIOP[]\\~ASDFGHJKL;'~~ZXCVBNM,./~"].map(x=>(o+=`/${b='_'.repeat(w=x<y?2:' 667699'[x=["Bs","Tab","Caps","Enter"][p++]||'Shift',p])}\\|`,m+=y+(x+'    ').slice(0,w)+y+y,n+=y+b+y+y,l+=' __'+b)[73]&&(k.push(l,m,n,o),l='',m=n=o=y),m=n=o=y='|',p=l=k=[])&&k.join`
`

❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更多高质量文章

关注公众号“DYBOY”,一个专注于CSS/JS开发技巧的前端公众号,更多前端小干货等着你喔!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值