Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 专题系列之正则表达式 #101

Open
yuanyuanbyte opened this issue Nov 24, 2021 · 0 comments
Open

JavaScript 专题系列之正则表达式 #101

yuanyuanbyte opened this issue Nov 24, 2021 · 0 comments

Comments

@yuanyuanbyte
Copy link
Owner

yuanyuanbyte commented Nov 24, 2021

本系列的主题是 JavaScript 专题,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

由浅入深的正则表达式教程,简单快速学习正则

前言

正则表达式,对大家来说既熟悉又陌生。熟悉是因为工作中有很多场景能用到,比如手机号、邮箱、密码等规则校验。

陌生则是因为正则表达式看上去就是一堆乱码,且一眼看上去很难看懂匹配规则。有时候在网上去找一个特定规则的正则表达式,搜出来的结果各不相同,执行效果更是不尽人意,想自己去修改,感觉也无从下手。

让我们通过这篇由浅入深的正则表达式教程,简单快速学习正则!!!

正则表达式是什么

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。

正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。

正则表达式是繁琐的,但它是强大的,学会之后的应用会让你除了提高效率外,会给你带来绝对的成就感。

许多程序设计语言都支持利用正则表达式进行字符串操作。

字符串匹配方法 match()

先看一个实例

以下实例从字符串 str 中找出数字:

从字符串 str 中提取数字部分的内容(匹配一次):

var str = "abc123def";
var patt1 = /[0-9]+/;
console.log(str.match(patt1))

输出结果:

['123', index: 3, input: 'abc123def', groups: undefined]

match() 方法检索返回一个字符串匹配正则表达式的结果。

重点在于match() 方法的返回值

  • 如果使用g标志,则将返回与完整正则表达式匹配的所有结果,但不会返回捕获组。
  • 如果未使用g标志,则仅返回第一个完整匹配及其相关的捕获组(Array)。 在这种情况下,返回的项目将具有如下所述的其他属性。

附加属性:

如上所述,匹配的结果包含如下所述的附加特性。

  • groups: 一个捕获组数组 或 undefined(如果没有定义命名捕获组)。
  • index: 匹配的结果的开始位置
  • input: 搜索的字符串.

看完match() 方法的返回值,是不是就搞明白为什么上面实例的输出结果是这么一长串数组了吧:['123', index: 3, input: 'abc123def', groups: undefined]

把上面的实例改为使用g标志:

var str = "abc123def";
var patt1 = /[0-9]+/g;
console.log(str.match(patt1))

输出结果:

['123']

字符串检查方法 test()

test() 方法用于检测一个字符串是否匹配某个模式.

如果字符串中有匹配的值返回 true ,否则返回 false

语法:RegExpObject.test(string)

var str="Hello world!";
//查找"Hello"
var patt=/Hello/g;
var result=patt.test(str);
console.log("返回值: " +  result); // 返回值: true
//查找 "Runoob"
patt=/Runoob/g;
result=patt.test(str);
console.log("返回值: " +  result);// 返回值: false

正则表达式语法

^ 和 $ 的用法和区别详解

通过一个实例学习基本的正则表达式语法

前面的例子是匹配字符串里的单词,现在我们来看一下如何匹配以某规则开头的字符串,以某规则结尾的字符串,以及整个字符串(即是不是 完全以某规则开头,某规则结尾的字符串)

匹配以数字开头,并以 abc 结尾的字符串。:

var str = "123abc";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1))

输出结果:

['123abc']

这里用到了一些基本的匹配规则:

  • ^ 表示必须匹配以^后面规则开头的字符串,如果该字符串不是以^后面的规则开头的,则直接返回null

  • $ 表示必须匹配以$前面规则结尾的字符串,如果该字符串不是以$前面规则结尾,则直接放回null

  • [0-9]+匹配多个数字, [0-9] 匹配单个数字,+ 匹配一个或者多个。

  • abc$匹配字母 abc 并以 abc 结尾

总结:

  • 单独使用^表示匹配以该规则开头的字符串;
  • 单独使用$表示匹配以该规则结尾的字符串;
  • 同时使用^$则表示整个字符串从开头到结尾必须符合匹配规则才算匹配成功,否则匹配失败返回null

通过修改上面的实例,验证一下我们总结的规则:

◾ 单独使用^表示匹配以该规则开头的字符串:

var str = "123abc范德萨发撒1112范德萨发撒";
var patt1 = /^[0-9]+abc/g;
console.log(str.match(patt1)) // ['123abc']

◾ 单独使用$表示匹配以该规则结尾的字符串:

var str = "123abc范德萨发撒1112范德萨发撒";
var patt1 = /[0-9]+abc$/g;
console.log(str.match(patt1)) // null

上面的字符串没有以[0-9]+abc结尾,所以匹配失败返回null;

var str = "fdsdfds123abc";
var patt1 = /[0-9]+abc$/g;
console.log(str.match(patt1)) // ['123abc']

上面的字符串以[0-9]+abc结尾,所以返回匹配结果;

◾ 同时使用^$则表示整个字符串从开头到结尾必须符合匹配规则才算匹配成功,否则匹配失败返回null:

匹配失败的例子:

var str = "fff123abc";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1)) // null
var str = "123abcfff";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1)) // null
var str = "fsd123abcfff";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1)) // null
var str = "fsd123ddabcfff";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1)) // null

匹配成功的例子:

var str = "123abc";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1)) // ['123abc']
var str = "12321554532532525abc";
var patt1 = /^[0-9]+abc$/g;
console.log(str.match(patt1)) // ['12321554532532525abc']

限定符

限定符 *、+、?、{n}、{n,} 和 {n,m} 的使用

例如:

  • runoo+b,可以匹配 runoob、runooob、runoooooob 等,+ 号代表前面的字符必须至少出现一次(1次或多次)。

  • runoo*b,可以匹配 runob、runoob、runoooooob 等,* 号代表前面的字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次)。

  • colou?r,可以匹配 color 或者 colour,? 问号代表前面的字符最多只可以出现一次(0次、或1次)。

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 *+?{n}{n,}{n,m} 共6种。

正则表达式的限定符有:

字符 描述
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 、 "does" 中的 "does" 、 "doxy" 中的 "do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。

普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

字符 描述
[ABC] 匹配 [...] 中的所有字符,例如 [aeiou] 匹配字符串 "google runoob taobao" 中所有的 e o u a 字母。
[^ABC] 匹配除了 [...] 中字符的所有字符,例如 [^aeiou] 匹配字符串 "google runoob taobao" 中除了 e o u a 字母的所有字母。
[A-Z] A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母。
. 匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\n\r]。
[\s\S] 匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行。
\w 匹配字母、数字、下划线。等价于 [A-Za-z0-9_]

以下正则表达式匹配一个正整数,[1-9]设置第一个数字不是 0,[0-9]* 表示任意多个数字:

/[1-9][0-9]*/

请注意,限定符出现在范围表达式之后。因此,它应用于整个范围表达式,在本例中,只指定从 0 到 9 的数字(包括 0 和 9)。

这里不使用 + 限定符,因为在第二个位置或后面的位置不一定需要有一个数字。也不使用 ? 字符,因为使用 ? 会将整数限制到只有两位数。

如果你想设置 0~99 的两位数,可以使用下面的表达式来至少指定一位但至多两位数字。

/[0-9]{1,2}/

上面的表达式的缺点是,只能匹配两位数字,而且可以匹配 0、00、01、10 99 的章节编号仍只匹配开头两位数字。

改进下,匹配 1~99 的正整数表达式如下:

/[1-9][0-9]?/

/[1-9][0-9]{0,1}/

❗ ❗ ❗ 重点

*+ 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。

例如,您可能搜索 HTML 文档,以查找在 h1 标签内的内容。HTML 代码如下:

<h1>RUNOOB-菜鸟教程</h1>

贪婪:下面的表达式匹配从开始小于符号 (<) 到关闭 h1 标记的大于符号 (>) 之间的所有内容。

/<.*>/

在这里插入图片描述

非贪婪:如果您只需要匹配开始和结束 h1 标签,下面的非贪婪表达式只匹配 <h1>

/<.*?>/

在这里插入图片描述

也可以使用以下正则表达式来匹配 h1 标签,表达式则是:

/<\w+?>/

在这里插入图片描述

通过在 *、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。

特殊字符

所谓特殊字符,就是一些有特殊含义的字符。

下表列出了正则表达式中的特殊字符(不包括前文讲到的)

字符 描述
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$
() 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \.
[ 标记一个中括号表达式的开始。要匹配 [,请使用 \[
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 \^
{ 标记限定符表达式的开始。要匹配 {,请使用 \{
` `

❗ ❗ ❗ 注意:^ 有两种意义

  • 匹配输入字符串的开始位置,前文已经详细介绍过
  • 在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合

元字符

下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\""\(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
. 匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用像`"(.
`x y`
[xyz] 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\w 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'。
\W 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'。

运算符优先级

正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。

相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:

运算符 描述
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \任何元字符、任何字符 定位点和序列(即:位置和顺序)
` `

常用的正则表达式

🔶 常见的特殊需求

◾ 最新的手机号匹配规则

手机号的正则算是最常用的。常见的手机号正则有
/^1[3456789]\d{9}$/、/^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/

各大运营商手机号码段(新):

运营商 号段
电信 133,149,153,173,174,177,180,181,189,191,193,199
移动 134,135,136,137,138,139,147,148,150,151,152,157,158,159,172,178,182,183,184,187,188,195,198
联通 130,131,132,145,146,155,156,166,175,176,185,186,196
广电 190,192,197
电信虚拟 162,1700,1701,1702
移动虚拟 165,1703,1705,1706
联通虚拟 167,1704,1707,1708,1709,171

根据运营商的最新号段,写出手机号验证的最新正则表达式

/^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/

◾ 身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X

身份证号码验证,包含两代身份证,第一代和第二代身份证

  • 第一代身份证15位,其编码规则顺序从左至右依次为6位数字地址码,6位数字出生年份后两位及日期,3位数字顺序码。

  • 第二代身份证18位,其编码规则顺序从左至右依次为6位数字地址码,8位数字出生年份日期码,3位数字顺序码,1位数字校验码(X有时会出现):

以北京市朝阳区一女性身份证号码为例,身份证号码所表示的含义如下图所示:

在这里插入图片描述

综上,身份证的正则表达式为:

(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)

◾ 匹配 16 进制颜色值: /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;

◾ 匹配日期,如 yyyy-mm-dd 格式

/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;

◾ 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)

◾ 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)

◾ Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

◾ 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):

^[a-zA-Z][a-zA-Z0-9_]{4,15}$

◾ 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):

^[a-zA-Z]\w{5,17}$

◾ 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间):

^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,10}$
**◾ 强密码(必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-10之间):**
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$

🔶 校验数字

  • 数字:^[0-9]*$
  • n位的数字:^\d{n}$
  • 至少n位的数字:^\d{n,}$
  • m-n位的数字:^\d{m,n}$
  • 零和非零开头的数字:^(0|[1-9][0-9]*)$
  • 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
  • 有两位小数的正实数:^[0-9]+(\.[0-9]{2})?$
  • 有1~3位小数的正实数:^[0-9]+(\.[0-9]{1,3})?$
  • 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
  • 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$

🔶 校验字符

  • 汉字:^[\u4e00-\u9fa5]{0,}$
  • 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
  • 长度为3-20的所有字符:^.{3,20}$
  • 由26个英文字母组成的字符串:^[A-Za-z]+$
  • 由26个大写英文字母组成的字符串:^[A-Z]+$
  • 由26个小写英文字母组成的字符串:^[a-z]+$
  • 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
  • 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
  • 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
  • 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
  • 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
  • 禁止输入含有~的字符:[^~\x22]+

参考

查看原文

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant