1-JavaScript - ECMAScript核心-正则
about
https://www.runoob.com/js/js-regexp.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
关于正则表达式
正则表达式(Regular Expression,简写regex、re),又称之为正则表示式、正则表示法、规则表达式等,它是计算机科学的一个概念。
正则表达式可以通过特殊字符+普通字符来实现一组规则,用于匹配字符串中符合规则的字串,从而达到将匹配的字串取值、替换的目的。
许多的程序设计语言(Java、JavaScript、Python......)都开发出自己的正则表达式引擎以支持利用正则表达式进行字符串操作。
如何学好正则表达式
无他,唯手熟尔。
对于新手来说,这里推荐几个在线的正则匹配网站,来帮助你快速编写正则规则:
另外,不同的编程语言对于正则的实现略有不同,但大部分上还是一致的,这点在实际开发中,是要注意的。
基本用法
创建一个正则对象,有两种方式:
// 示例,编写正则规则,用来判断一个字符串中是否包含指定字符 a
// 法1,通过new声明
let re1 = new RegExp('a');
// 通过字面量的形式
let re2 = /a/;
// console.log(re1); // /a/
// console.log(re2); // /a/
但要注意的是,正则匹配中,严格区分大小写,如果不区分大小写的话,需要使用i
模式:
// i表示不区分大小写, i的两种用法
let re3 = new RegExp('a', 'i');
let re4 = /a/i;
// test方法用来检测字符串是否包含符合正则表达式的子串,包含返回true,否则返回false
console.log(re3.test('ABC')); // true
console.log(re3.test('abc')); // true
console.log(re4.test('ABC')); // true
console.log(re4.test('abc')); // true
// match,以数组的形式返回符合正则条件的结果,没有结果返回null
let s = 'school w3school +';
console.log(s.match(/[+]/)); // [ '+', index: 16, input: 'school w3school +', groups: undefined ]
修饰符
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#通过标志进行高级搜索
修饰符,即指的匹配模式,比如正则匹配中,严格区分大小写,如果不区分大小写的话,需要使用i
模式。
来看都有哪些修饰符:
标志 | 描述 |
---|---|
g |
全局搜索。 |
i |
不区分大小写搜索。 |
m |
多行搜索。 |
s |
允许 . 匹配换行符。 |
u |
使用unicode码的模式进行匹配。 |
y |
执行“粘性(sticky )”搜索,匹配从目标字符串的当前位置开始。 |
/*
* i模式,执行对大小写不敏感的匹配。
* */
// 默认是大小写敏感
// let re1 = /a/;
// console.log(re1.test('A')); // false
// console.log(re1.test('a')); // true
// 想要取消大小写敏感,需要使用i模式,基础用法
// let re2 = new RegExp('a', 'i');
// let re3 = /a/i;
// console.log(re2.test('A')); // true
// console.log(re3.test('A')); // true
/*
* g模式,执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
* */
// let re4 = /a/g;
// global 属性用于返回正则表达式是否具有标志 "g"
// 它声明了给定的正则表达式是否执行全局匹配。
// 如果 g 标志被设置,则该属性为 true,否则为 false。
// console.log(re4.global); // true
基础元字符
元字符(Metacharacter)是拥有特殊含义的字符:
字符 | 描述 | 示例 |
---|---|---|
[] |
一组字符 | "[a-m]" |
\ |
示意特殊序列(也可用于转义特殊字符) | "\d" |
. |
任何字符(换行符除外) | "he..o" |
^ |
起始于 | "^hello" |
$ |
结束于 | "world$" |
* |
零次或多次出现 | "aix*" |
+ |
一次或多次出现 | "aix+" |
{} |
确切地指定的出现次数 | "al{2}" |
` | ` | 两者任一 |
() |
捕获和分组 |
由上面基础的元字符或者其他组成形式,来构造完成正则表达式,最终用来查找字符串中符合正则表达式的子串。
字符集
字符集,也称之为集合或方括号,是一对方括号 [] 内的一组字符,具有特殊含义:
集合 | 描述 |
---|---|
[arn] | 返回一个匹配项,其中存在指定字符(a,r 或 n)之一 |
[a-n] | 返回字母顺序 a 和 n 之间的任意小写字符匹配项 |
[^arn] | 取反,返回除 a、r 和 n 之外的任意字符的匹配项 |
[0123] | 返回存在任何指定数字(0、1、2 或 3)的匹配项 |
[0-9] | 返回 0 与 9 之间任意数字的匹配 |
[a-zA-Z] | 返回字母顺序 a 和 z 之间的任何字符的匹配,小写或大写 |
[+] | 在集合中,+、*、.、|、()、$、{} 没有特殊含义,因此 [+] 表示:就是简单的匹配字符+ |
返回一个匹配项,其中存在指定字符(a,r 或 n)之一:[arn]
let s = 'school w3school';
console.log(s.match(/[s3o]/)); // [ 's', index: 0, input: 'shool w3school', groups: undefined ]
返回字母顺序 a 和 n 之间的任意小写字符匹配项:[a-n]
let s = 'school w3school';
console.log(s.match(/[a-s]/)); // [ 's', index: 0, input: 'school w3school', groups: undefined ]
取反,返回除 a、r 和 n 之外的任意字符的匹配项:[^arn]
let s = 'school w3school';
console.log(s.match(/[^s]/)); // [ 'c', index: 1, input: 'school w3school', groups: undefined ]
返回存在任何指定数字(0、1、2 或 3)的匹配项:[0123]
let s = 'school w3school';
console.log(s.match(/[123]/)); // [ '3', index: 7, input: 'shool w3school', groups: undefined ]
返回 0 与 9 之间任意数字的匹配:[0-9]
let s = 'school w3school';
console.log(s.match(/[0-9]/)); // [ '3', index: 8, input: 'school w3school', groups: undefined ]
返回字母顺序 a 和 z 之间的任何字符的匹配,小写或大写:[a-zA-Z]
let s = 'school w3school';
console.log(s.match(/[a-zA-Z]/)); // [ 's', index: 0, input: 'school w3school', groups: undefined ]
在集合中,+、*、.、|、()、$、{} 没有特殊含义,因此 [+] 表示:就是简单的匹配字符:[+]
+
let s = 'school w3school +';
console.log(s.match(/[+]/)); // [ '+', index: 16, input: 'school w3school +', groups: undefined ]
练习,写正则,匹配手机号:
let s1 = '18211301743';
let s2 = '13211301743';
let s3 = '14211301743';
console.log(s1.match(/1[3-9][0-9]{9}/));
console.log(s2.match(/1[3-9][0-9]{9}/));
console.log(s3.match(/1[3|5-9][0-9]{9}/));
特殊序列
特殊序列也算元字符的一种,指的是 \
后跟下表中的某个字符,拥有特殊含义。
以下列出常见的:
字符 | 描述 | 备注 |
---|---|---|
\b |
返回指定字符位于单词的开头或末尾的匹配项 | |
\B |
返回指定字符存在的匹配项,但不在单词的开头(或结尾处) | |
\d |
返回字符串包含数字的匹配项(数字 0-9) | |
\D |
返回字符串不包含数字的匹配项 | |
\s |
返回字符串包含空白字符的匹配项 | |
\S |
返回字符串不包含空白字符的匹配项 | |
\w |
返回一个匹配项,其中字符串包含任何单词字符 (从 a 到 Z 的字符,从 0 到 9 的数字和下划线 _ 字符) |
|
\W |
返回一个匹配项,其中字符串不包含任何单词字符 | |
\r |
匹配一个回车符 | |
\n |
匹配一个换行符 |
更多参考:
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
- https://www.w3school.com.cn/jsref/jsref_obj_regexp.asp
返回指定字符位于单词的开头或末尾的匹配项:\b
let s = 'school w3school';
console.log(s.match(/\bschool/)); // [ 'school', index: 0, input: 'school w3school', groups: undefined ]
返回指定字符存在的匹配项,但不在单词的开头(或结尾处):\B
let s = 'school w3school';
console.log(s.match(/\Bschool/)); // [ 'school', index: 9, input: 'school w3school', groups: undefined ]
返回字符串包含数字的匹配项(数字 0-9):\d
let s = 'school w3school';
console.log(s.match(/\d/)); // [ '3', index: 8, input: 'school w3school', groups: undefined ]
返回字符串不包含数字的匹配项:\D
let s = 'school w3school';
console.log(s.match(/\D/)); // [ 's', index: 0, input: 'school w3school', groups: undefined ]
// 如果你对上面的结果只返回第一个字符有疑问的话,这是因为默认匹配是非全局模式,下面是使用了全局模式后的结果
let s = 'school w3school';
console.log(s.match(/\D/g));
/*
[
's', 'c', 'h', 'o',
'o', 'l', ' ', 'w',
's', 'c', 'h', 'o',
'o', 'l'
]
*/
返回字符串包含空白字符的匹配项:\s
let s = 'school w3school';
console.log(s.match(/\s/)); // [ ' ', index: 6, input: 'school w3school', groups: undefined ]
返回字符串不包含空白字符的匹配项:\S
let s = 'school w3school';
console.log(s.match(/\S/)); // [ 's', index: 0, input: 'school w3school', groups: undefined ]
返回一个匹配项,其中字符串包含任何单词字符:\w
等价于[A-Za-z0-9_]
。
let s = 'school w3school';
console.log(s.match(/\w/)); // [ 's', index: 0, input: 'school w3school', groups: undefined ]
console.log('0ab'.match(/\w/)); // [ '0', index: 0, input: '0ab', groups: undefined ]
console.log('_ab'.match(/\w/)); // [ '_', index: 0, input: '_ab', groups: undefined ]
返回一个匹配项,其中字符串不包含任何单词字符:\W
等价于[^A-Za-z0-9_]
。
let s = 'school w3school';
console.log(s.match(/\W/)); // [ ' ', index: 6, input: 'school w3school', groups: undefined ]
匹配一个回车符:\r
let s = 'school w3scho\rol';
console.log(s.match(/\r/)); // [ '\r', index: 13, input: 'school w3scho\rol', groups: undefined ]
匹配一个换行符:\n
let s = 'school w3scho\nol';
console.log(s.match(/\n/)); // [ '\n', index: 13, input: 'school w3scho\nol', groups: undefined ]
量词
量词约束字符或者字符组的重复规则。
符号 | 描述 | 备注 |
---|---|---|
* |
重复零次或多次(任意次) | |
+ |
重复一次或者多次(至少出现一次) | |
? |
重复零次或者一次 | |
{n} |
重复n次 | |
{n,} |
匹配前一个字符至少出现了n次 | 没有{,n} 的用法 |
{n,m} |
匹配前面的字符至少n次,最多m次 |
重复零次或多次:*
// 以a开头,匹配b最少0次,或者多次,最后必须以c结尾
let re1 = /ab*c/;
console.log(re1.test('b')); // false 不能少了开头的a和结尾的c
console.log(re1.test('ac')); // true b出现0次,可以
console.log(re1.test('abc')); // true b出现1次,可以
console.log(re1.test('abbbc')); // true b出现多次,可以
重复一次或者多次:+
// 以a开头,匹配b最少1次,或者多次,最后必须以c结尾
let re1 = /ab+c/;
console.log(re1.test('b')); // false 不能少了开头的a和结尾的c
console.log(re1.test('ac')); // false b出现0次,不行
console.log(re1.test('abc')); // true b出现1次,可以
console.log(re1.test('abbbc')); // true b出现超过1次,可以
重复零次或者一次:?
// 以a开头,匹配b最少0次,或者1次,最后必须以c结尾
let re1 = /ab?c/;
console.log(re1.test('b')); // false 不能少了开头的a和结尾的c
console.log(re1.test('ac')); // true b出现0次,也没问题
console.log(re1.test('abc')); // true b出现1次,也行
console.log(re1.test('abbc')); // false b出现超过1次,不行
重复n次:{n}
// 以a开头,匹配b最少出现的次数必须等于2次,最后必须以c结尾
let re1 = /ab{2}c/;
console.log(re1.test('b')); // false 不能少了开头的a和结尾的c
console.log(re1.test('ac')); // false b至少出现2次
console.log(re1.test('abc')); // false b至少出现2次
console.log(re1.test('abbc')); // true b出现2次,才行
console.log(re1.test('abbbbc')); // false b出现的大于2次也不行
匹配前一个字符至少出现了n次:{n,}
// 以a开头,匹配b最少出现的次数必须大于等于2次,最后必须以c结尾
let re1 = /ab{2,}c/;
console.log(re1.test('b')); // false 不能少了开头的a和结尾的c
console.log(re1.test('ac')); // false b至少出现2次
console.log(re1.test('abc')); // false b至少出现2次
console.log(re1.test('abbc')); // true b出现2次
console.log(re1.test('abbbbc')); // true b出现的大于2次
匹配前面的字符至少n次,最多m次:[n,m]
// 以a开头,匹配b最少1次,最多2次,最后必须以c结尾
let re1 = /ab{1,2}c/;
console.log(re1.test('b')); // false 不能少了开头的a和结尾的c
console.log(re1.test('ac')); // false b至少出现1次
console.log(re1.test('abc')); // true b至少出现1次
console.log(re1.test('abbc')); // true b出现2次
console.log(re1.test('abbbc')); // false b出现的次数超过了2
相关方法
test/exec
test和exec方法都是用来检测字符串是否包含符合正则表达式的子串,其区别就是:
- test,匹配到了返回true,否则返回false。
- exec,匹配到了返回数组,否则返回null。
let s1 = /o{2}l/;
let s2 = /o{3}l/;
console.log(s1.test('school w3school')); // true
console.log(s1.exec('school w3school')); // [ 'ool', index: 3, input: 'school w3school', groups: undefined ]
console.log(s2.test('school w3school')); // false
console.log(s2.exec('school w3school')); // null
search
search用来搜索字符串中是否含有符合正则规则的子串,找到即返回从左到右第一次出现的位置索引,否则返回-1。
let s = 'school w3school';
console.log(s.search(/o{2}l/)); // 3 匹配到了,返回第一个匹配项的索引
console.log(s.search(/o{3}l/)); // -1 没有匹配到,返回 -1
match
match根据正则规则,以数组的形式返回匹配结果,没有结果返回null。
let s = 'school w3school';
console.log(s.match(/o{2}l/)); // [ 'ool', index: 3, input: 'school w3school', groups: undefined ]
console.log(s.match(/o{3}l/)); // null
split
split根据正则规则的结果进行分割,如果没有符合正则条件的,则不分割,以数组的形式,将原字符串返回。
let s = 'school w3school';
// 以ool进行分割
console.log(s.split(/o{2}l/)); // [ 'sch', ' w3sch', '' ]
// 第二个参数是可选的,指定返回数组的最大长度
console.log(s.split(/o{2}l/, 1)); // [ 'sch' ]
// 没有符合正则条件的,则不分割,以数组的形式,将原字符串返回
console.log(s.split(/o{3}l/)); // [ 'school w3school' ]
replace
replace根据正则规则,将符合条件的子串替换成指定字符串,返回替换后的字符串,对原字符串没有影响。
let s = 'school w3school';
// 通过下面的替换结果,发现它只替换符合正则规则的第一个匹配项
console.log(s.replace(/o{2}l/, 'OOL')); // schOOL w3school
// 想要替换所有符合条件的,需要使用全局模式
console.log(s.replace(/o{2}l/g, 'OOL')); // schOOL w3schOOL
// replace返回替换后的字符串,对原字符串没有影响。
console.log(s); // school w3school
贪婪匹配
所谓贪婪匹配就是匹配重复字符是尽可能多的匹配,来看示例:
let s = 'aaaab';
console.log(s.match(/a+/)); // [ 'aaaa', index: 0, input: 'aaaab', groups: undefined ]
非贪婪匹配(也叫做惰性匹配)就是尽可能少的匹配,使用也非常简单,那就是在量词后面加?
即可:
let s = 'aaaab';
// 贪婪匹配
console.log(s.match(/a+/)); // [ 'aaaa', index: 0, input: 'aaaab', groups: undefined ]
// 非贪婪匹配
console.log(s.match(/a+?/)); // [ 'a', index: 0, input: 'aaaab', groups: undefined ]
但有的时候,贪婪匹配和非贪婪匹配也可以返回同样的结果:
let s = 'aaaab';
console.log(s.match(/a+b/)); // [ 'aaaab', index: 0, input: 'aaaab', groups: undefined ]
console.log(s.match(/a+?b/)); // [ 'aaaab', index: 0, input: 'aaaab', groups: undefined ]
上面示例返回同样结果的原因,首先量词+
匹配一次或者多次,那么第一个返回就不用解释了,很正常。
第二个返回结果之所以不像上上个例子中只匹配一个a
就返回了,这是因为,虽然加了?
,但它还要匹配b
所以,匹配到第一个a
后,还不得不往后接续寻找,直到匹配到了b
停下来,返回结果。
但如果在寻找b
的途中,发现a
后面没有直接跟b
的,就什么也匹配不到了,如下示例:
let s1 = 'aaaacb';
console.log(s1.match(/a+?b/)); // null