正则表达式(基础篇)
一、正则表达式的概念
正则表达式(Regular Expression,常简写为RegExp或RE),又称规则表达式,通常用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是为字符串处理而生的,它使用特定的格式,来检测字符串中的某个特定模式,如以下的表达式用来检测字符串是否是三个数字构成的:
/^\d{3}$/
我们逐个来分析上面的每个部分。
首先,两侧的斜线/ ... /
是正则表达式的特定标志,它表示中间的部分是一个正则表达式。^
是匹配字符串的开头,但它并不会消耗字符。\d
表示要匹配一个0-9
的数字。{3}
表示要将上述规则匹配三次。而$
表示要匹配字符串的结尾。
这个正则表达式所要描述的规则是这样的:从字符串的开头到结尾,中间必须是三个数字构成。
现在我们拿这个正则表达式,来检测一下下面的几个字符串是否符合该模式吧:
/^\d{3}$/.test('369'); // true
/^\d{3}$/.test('023'); // true
/^\d{3}$/.test('1234'); // false
/^\d{3}$/.test('1d3'); // false
/^\d{3}$/.test('abcd'); // false
可以看到,只有被检测的字符串是由三个数字构成的时候,检测结果才是true
,否则一律为false
。有了这个正则表达式,我们就可以很容易检测任意一个字符串是否是三个数字构成的。
除了检测整个字符串是否符合某个模式外,正则表达式还可以从字符串中捕获出符合某个特定模式的子字符串。比如我们想从一个字符串中提取出任意相连的三个数字,上面的正则表达式可以进行如下改写:
/\d{3}/
这时我们不再关心这个字符串是否只包含三个数字,而是关心字符串中是否存在三个连续数字
这样的模式。此时可以像下面一样使用上述正则表达式:
let reg = /\d{3}/;
reg.exec('abc543as');
// ["543", index: 3, input: "abc543as", groups: undefined]
我们的正则表达式成功从字符串'abc543as'
中检索出了'543'
这个由三个连续数字构成的子串,并给出了它的索引index
和原始字符串(groups输出的是捕获组,后面我们会介绍)。
除了这两种情况,字符串的replace
方法、match
方法等也会用到正则表达式。replace
用于替换字符串中符合某个模式的子串,match
的作用与exec
类似,但是具体用法上有一些差别。
理解了正则表达式的基本概念后,我们就来看一下正则表达式为我们提供了哪些基础模式,它们是我们学习正则表达式最基础的知识。
二、正则表达式的模式单元
1. \
用来将下一个字符标记为特殊字符、原义字符、向后引用或转义字符。
比如我们是没办法直接在电脑屏幕上输出换行符的,因此正则表达式规定可以通过\n
这样转义的方法来表达换行符。
再比如,上面我们讲到\d
可以用来匹配数字。注意,只有d
的前面带着一个反斜线组合使用时,它才可以匹配数字,否则它就只能匹配字母d
。正则表达式中这种用法非常常见,下面我们会介绍很多使用\
表达特定字符类型的模式单元。
2. ^
匹配字符串的开头位置。
注意,它本身并不匹配任何字符,只是一个位置标志。比如:
/^\d/.test('sd23a'); // false
/\d/.test('sd23a'); // true
可以看到,这两个模式是不一样的。加了^
的表达式是要匹配一个数字,并且这个数字必须是在字符串开头的位置;没有^
时则没有要求。
3. $
匹配字符串的结尾位置。
原理同上。如/\d$/
这个表达式要求字符串必须是数字结尾的,而/\d/
则允许数字出现在字符串的任意位置:
/\d$/.test('sd23a'); // false
/\d/.test('sd23a'); // true
4. *
匹配前面的子表达式零次或多次。
比如,下面的表达式可以匹配任意多个数字构成的字符串,包括0个:
let reg = /^\d*$/;
reg.test(''); // true
reg.test('23454'); // true
reg.test('34a34'); // false
除了匹配单个子表达式,还可以是一个较为复杂的表达式:
/^(\d{3})*$/
它会匹配零至多组由三个数字构成的字符串,也就是数字的数量必须是3的倍数,如:
/^(\d{3})*$/.test(''); // true,0个数字
/^(\d{3})*$/.test('324'); // true,3个数字
/^(\d{3})*$/.test('345756'); // true,6个数字
/^(\d{3})*$/.test('56'); // false,2个数字
/^(\d{3})*$/.test('a34'); // false,含有非数字字符
5. +
匹配前面的子表达式一次或多次。
它的用法与*
很像,只是它要求子表达式至少要匹配一次。如:
let reg = /^\d+$/;
reg.test('23'); // true
reg.test(''); // false
可以看到,当字符串为空时,使用+
就不能匹配了。
6. ?
匹配前面的子表达式0次或1次。
与*
和+
类似,只是它要求前面的子表达式要么不出现,要么只出现一次。如下面的表达式只能匹配0或1个数字:
let reg = /^\d?$/;
reg.test(''); // true
reg.test('6'); // true
reg.test('23'); // false
7. {n}、{n,}、{n, m}
匹配子表达式n次、至少n次、n至m次。
{n}
匹配表达式n次,如/o{2}/
可以匹配food
中的字符串oo
但不能匹配Bob
中的o
。
{n,}
匹配表达式至少n次,如/o{2,}/
可以匹配food
、oooh
中的oo
和ooo
,但是不能匹配Bob
中的o
,因为这里o
出现的次数少于2次。
{n, m}
匹配表达式至少n次,至多m次。如/o{2, 3}/
可以匹配food
、oooh
中的oo
和ooo
,但是不能匹配Bob
中的o
,因为这里o
出现的次数不在2-3次之间。不过当o
出现的次数大于3次的时候不会导致检测失败,只是超过3个的字符o
会被截断。
这些修饰单元也可以修饰括号包含起来的更复杂的子表达式。
8. 限制字符后面的’?’
当?
位于限制字符后面时,它将以非贪婪模式匹配字符串。
上面我们介绍的*、+、?、{n}、{n,}、{n,m}
都属于限制字符,它们的作用是限制子表达式的数量。默认地,这些限制字符会以贪婪模式去匹配字符串,也就是尽量多地匹配字符串,如:
/\d+/.exec('2342');
// ["2342", index: 0, input: "2342", groups: undefined]
可以看到,我们的正则表达式希望匹配数字至少一次。但是这里有4个数字,到底要匹配几次呢?显然,1、2、3、4次都是可以的。
由于表达式默认匹配尽量多的字符串,因此它会匹配所有4个数字。
但是如果在+
的后面加上一个?
,表达式将启用非贪婪模式,即匹配尽量少的字符:
/\d+?/.exec('2342');
// ["2", index: 0, input: "2342", groups: undefined]
可以看到,这时表达式只匹配了一个2
。其他的限制字符也是同样的道理。
9. .
该字符就是英文的句号,它匹配除\n
之外的任意字符。
如:
/./.test('2'); // true
/./.test('s'); // true
/./.test('\t'); // true,这里的\t是制表符,不是两个字符
/./.test('\n'); // false,不能匹配换行符
可以看到,.
可以匹配除换行符\n
以外的任意字符。如果想要匹配任意字符,可以使用[.\n]
,关于中括号的用法后面会介绍。
10. (patten)
匹配某个模式并获取匹配结果。
该语法用于从原字符串中捕获符合某个模式的子字符串。假如我们希望捕获某字符串中的多个连续数字组合,可以把我们要匹配的模式包含在括号内,然后从原生对象RegExp的静态属性中获取。如:
let reg = /(\d{3}).*(\d{3})/g;
let string = 'as123bs456';
string.match(reg);
RegExp.$1; // '123'
RegExp.$2; // '456'
这里定义了两个需要捕获的模式,即两组三个连续的数字。第一个模式(\d{3})
捕获到了字符串'123'
,第二个模式(\d{3})
捕获到了字符串'456'
。
注意,RegExp的静态属性最多只能存储9
个捕获组(即RegExp.$1 - $9
),并且一旦进行了下次匹配,上次的捕获结果就会被覆盖。如果想要在正则表达式中使用上次的捕获结果,需要使用\n
的格式,注意,这里的n
代指数字,而不是字母n。如:
let reg = /(\d{3}).*\1/g;
这里的\1
代指对前面第一个捕获到的字符串的引用,也就是(\d{3})
所捕获到的字符串。因此该表达式要匹配的是类似于'123sd123'
这样的字符串,即至少存在两组相同的三数字组。如:
reg.test('s345v345'); // true
reg.test('345345'); // true
reg.test('345v345w345'); // true
reg.test('ads345vv346'); // false
reg.test('ads34vv34'); // false
11. (?:patten)
匹配但不捕获该模式。
这里在patten的前面加了字符?:
,表示只匹配括号内的模式,但不计入捕获组。换句话说,此时的括号只是用作普通的括号,不再具有捕获模式的能力。如:
/industr(?:y|ies)/.exec('industries')
RegExp.$1; // ''
如果上述括号内没有?:
,则这里捕获到的ies
应当被计入RegExp.$1,加了该修饰符后它没有被捕获。该模式可以防止由括号的使用导致意外捕获。此时,下面的两种写法是完全等价的:
/industr(?:y|ies)/ <=> /industry|industries/
12. (?=patten)
对后续字符串进行预查,只有后面的字符串符合预查中的模式,当前字符串才算匹配上,不获取匹配结果,也不消耗字符串。
举个例子:
/win(?=10)/
该表达式要匹配的字符串是win
,但我们要捕获的必须是后面紧跟着10
的字符串win
,否则不予匹配。下次匹配时仍然会继续从10
开始匹配,也就是说上次的匹配并没有把字符串10
算进去。如:
/win(?=10)(\d+)/.test('win10'); //true
RegExp.$1; // '10'
可以看到,尽管在匹配win(?=10)
已经对字符串10
进行了预查,但是(\d+)
仍然能匹配到字符串10
,这是因为上次的检查只是预检查,并没有消耗掉这两个数字。如果是下面的写法,则无法匹配:
/win10(\d+)/.test('win10'); // fasle
这里(\d+)
前面的部分已经匹配完了所有的字符(win10
),于是(\d+)
没有匹配到任何字符,导致最终匹配失败。
13. (?!patten)
该模式与上一个正好相反,只有当后续字符串不符合某个模式时才算配成功,同样不消耗字符串,也不进行捕获。
如/win(?!10)/
只会匹配后面没有紧跟着字符串10
的字符串win
,但是不影响字符串10
参与后续的匹配过程。
14. x|y
逻辑‘或’,表示匹配左侧模式,或者匹配右侧的模式。
如:
/industr(?:y|ies)/
表示字符串industr
后面跟着字符y
,或者ies
。因此industry
和industries
都符合该表达式。
15. []
匹配列出的字符中的任意字符。
如[abc123]
可以匹配a、b、c、1、2、3这六个字符中的任意一个(注意,它只匹配一个字符,中括号列出的是字符的范围)。
中括号内可以使用范围字符,如[a-zA-Z0-9]
匹配的是所有的小写、大写字母和数字字符。a-z
的含义是匹配ASCII码在字符a和字符z之间的所有字符,由于所有的小写字母的ASCII码值是连续的,因此这里可以匹配到所有的小写字母。
中括号内可以出现特殊字符,如[.\n]
。由于.
可以匹配除\n
之外的所有字符,在该范围上再加入字符\n
,就可以匹配所有的字符了。
16. [^]
作用与上述模式相反,匹配不在列出的字符内的任意字符。
如[^a-z]
匹配的是不在a-z
范围内的任意字符。
17. \b、\B
分别匹配单词边界和非单词边界。
\b
匹配单词边界。如:
let reg = /(as)\b/;
reg.test('was'); // true
reg.test('was you'); // true
reg.test('aswww'); // false
reg.test('wasw'); // false
这里\b
用来匹配单词的边界,因此只有as的后面是单词边界时,它才会被匹配。
\B
只在该字符串不在单词边界时才会被匹配,与\b
刚好相反。如:
reg = /(as)\B/;
reg.test('wasw'); // true
reg.test('was'); // false
第一个字符串中as
不位于单词边界,匹配成功,第二个位于单词边界,则匹配失败。
18. \d、\D
分别匹配数字、非数字。
\d
用于匹配一个数字,这个我们上面已经见到了。
\D
相反,用于匹配一个非数字的字符。如:
/\d/.test('1'); // true
/\d/.test('a'); // false
/\D/.test('1'); // false
/\D/.test('a'); // true
19. \f、\n、\r、\t、\v
这几个都是特殊字符,分别是换页符、换行符、回车符、制表符和垂直制表符,在进行文件字符串匹配时可能会用到。这里不再详述。
20. \s、\S
匹配空白字符、非空白字符。
上述五个特殊字符都属于空白字符,另外,空格也属于空白字符,因此\s
等价于[ \f\n\r\t\v]
。
\S
匹配非空白字符,不属于上面6个字符的所有字符,都可以被\S
匹配到。
21. \w、\W
匹配包括下划线在内的单词字符、非单词字符。
\w
等价于[A-Za-z0-9_]
,而\W
则匹配这些字符以外的所有字符。
22. \cx
匹配由x指定的控制字符。
这里的x代指a-z或A-Z中的一个字母,如\cM
可以匹配Control-M
或回车符。如果\c
后面不是一个字母,那么\c
等价于字符c
。
23. \num
斜线后紧跟一个数字,表示对前面第num个被捕获字符串的引用。当num是一个0-7之间的数值时,它还可以匹配一个八进制数字。
如下面的表达式匹配的是两个挨着的相同字符:
let reg = /(.)\1/;
reg.test('aa'); // true
reg.test('sraav'); // true
reg.test('aba'); // false
这里的\1
代指前面(.)
捕获到的字符,而(.)
可以匹配\n
以外的任意字符,因此这个表达式就可以匹配所有除\n
以外的任意连着的相同字符。
该语法匹配八进制数字的用法如下:
/\2/.test('\02'); // true
注意,这里的\02
只代表一个八进制的2
,\0
是八进制数的标志。
24. \un
匹配一个十六进制的Unicode字符,其中n是由四个数字或字母组成的。
如\u00A9
匹配版权符号 (©),00A9
就是版权符号对应的Unicode值。该方法可以匹配任意的中文字符,因为每个中文字符都有对应的Unicode值。如:
/\u6770/.test('杰'); // true
各个字符的Unicode值请自行查阅Unicode字符表。
25. (?<=patten)
对前置字符串进行预查,只有前面的字符串符合预查中的模式,当前字符串才算匹配上,不获取匹配结果,也不消耗字符串。它与(?=patten)
是对应的,一个是向前预查,一个是向后预查。
如:
var reg = /(?<=95)windows/
该表达式要求只有前面是字符串95
的字符串windows
才能被匹配:
reg.test('95windows'); // true
reg.test('2000windows'); // false
第一个字符串的windows
前面是字符串95
,因此它匹配成功,第二个则失败了。
25. (?<!patten)
与上述模式相反,只有前置字符不符合预查的模式时,当前字符才算被匹配上。如:
var reg = /(?<!95)windows/
上面的表达式只能匹配前面没有字符串95
的字符串windows
:
reg.test('2000windows'); // true
reg.test('95windows'); // false
第一个字符串的windows
前面不是字符串95
,因此它匹配成功,第二个前面是95
,因此检测失败。
26. 限制字符后面的’+’
在限制字符*、+、?、{n}、{n,}、{n,m}
后面加+
将启用独占模式,它是与贪婪模式和非贪婪模式并列的一种匹配模式。
独占模式与贪婪模式一样,会尽量多地匹配字符串,但是在后续的匹配失败时,它不会像贪婪模式一样发生回溯。举个例子:
var reg = /ab{1,3}+bc/
reg.test('abbc'); // false
在{1,3}
后面加上+
表示启用独占模式,此时它会尽可能多地匹配字符b
,因此会消耗这里的两个b
。接下来引擎尝试用剩下的字符c
与正则表达式匹配,发现匹配失败。如果在贪婪模式下,引擎会进行回溯,也就是尝试吐出之前匹配的一个字符b
进行重新比对,这样就可以匹配成功。但是在独占模式下不会释放之前已经匹配到的字符,因此最终匹配失败。
三、模式修饰符
模式修饰符定义了该正则表达式在匹配时采用何种方式,如/i
表示在匹配时要忽略大小写。模式修饰符写法如下:
/\w/i // 单个修饰符
/\w/igm // 多个修饰符,顺序无关
下面是所有的模式修饰符。
1. /g
全局匹配模式。启用了全局模式后,正则表达式的匹配是有状态的,即它会记录上次的匹配位置,以便继续下次匹配。
默认情况下,正则表达式在完成一次匹配后就会结束匹配,如:
'123a456'.match(/\d{3}/);
// ["123", index: 0, input: "123a456", groups: undefined]
上面的正则表达式只匹配到了第一个符合条件的字符串123
,却没有去匹配后面符合条件的字符串456
。而加了/g修饰符后,匹配结果如下:
'123a456'.match(/\d{3}/g);
// ["123", "456"]
可以看到,所有符合条件的字符串都被捕获到了。
2. /i
不区分大小写。
即不对英文字符区分大小写,如:
/[a-z]/.test('A'); // false
/[a-z]/i.test('A'); // true
3. /m
多行匹配。当需要匹配的字符串是个多行文本时,/m可以改变^和$的行为。
默认情况下,即使一个字符串是由多行文本组成的,整个字符串也只有一个开头和一个结束位置。但是使用/m开启多行模式后,每行都有一个开头和一个结尾位置。如:
/^a[.\n]*c$/.test('abc\n123'); // false
字符串'abc\n123'
看起来大致是下面的结构:
^abc
123$
而/^a.*c$/
要匹配的是以a开头,c结尾的任意字符串。由于没有开启多行匹配,这里的结尾指的是整个字符串的结尾,即字符3
后面的位置。因此这里匹配失败了,因为整个字符串的尾部是字符3
。
但是开启多行匹配后,字符串'abc\n123'
则相当于下面的结构:
^abc$
^123$
即每行都相当于有了一个开始符合结束符,此时上面的检查就会返回true:
/^a[.\n]*c$/m.test('abc\n123'); // true
此时的$匹配到的是字符c
后面的结束符,而不是整个字符串最后的结束符。
4. /s
单行匹配模式。
单行匹配模式下,字符串只有一个开始符和一个结束符。有人会问,那不是跟没有任何修饰符一样的吗?
其实还是有一点差别的。/s会改变模式单元.
的行为,使得它可以匹配\n
,如:
'ab\ncd'.match(/.*/);
// ["ab", index: 0, input: "ab", groups: undefined]
'ab\ncd'.match(/.*/s);
// ["ab↵cd", index: 0, input: "ab↵cd", groups: undefined]
此时.
对换行符\n
也是有效的。
5. /y
是否启用粘性模式。
启用了粘性模式后,该正则表达式必须从lastIndex指定的位置开始匹配,lastIndex的值可以手动修改,默认是0,并且会在匹配失败时重置为0。如:
var str = '#foo#';
var reg = /foo/y;
reg.lastIndex = 0;
reg.test(str); // false,字符串的0位置是#,与f不匹配
reg.lastIndex = 1;
reg.test(str); // true
reg.lastIndex = 3;
reg.test(str); // false
reg.lastIndex; // 0,上次的匹配失败导致lastIndex被重置
此时只有把lastIndex置为1才可匹配成功,请他情况均会匹配失败。即此时由lastIndex人为指定了匹配位置,该位置匹配失败则立即判定为失败,不再继续匹配。
此外还有两个修饰符/x和/e,它们在JavaScript中是无效的,因此这里不再介绍。
四、正则表达式的用法
这里我们只介绍最常见的用法。
1. 正则表达式的原型方法
正则表达式主要有两个用于匹配的原型方法,test
和exec
。
(1). test
用于检测传入的字符串是不是符合正则表达式所规定的模式,只要检测到一个符合要求的子串,就返回true,否则返回false。如:
let reg = /\d+/;
reg.test('12sd'); // true
reg.test('af'); // false
(2). exec
exec用于执行正则表达式,输出匹配结果及捕获组。如:
let reg = /(\d+)a/;
reg.exec('12a34');
// ["12a", "12", index: 0, input: "12a34", groups: undefined]
// '12a'是匹配到的完整字符串,'12'是捕获组捕获的结果
exec方法返回的结果是一个数组,第0项是匹配到的符合正则表达式的完整字符串;从第一项往后,都是由捕获组捕获到的字符串。
在没有设置匹配模式为/g或/y的情况下,正则表达式的匹配是无状态的,也就是说它不会记录上次的匹配位置。如:
let reg = /\d+/;
let string = '12a34';
reg.exec(string);
// ["12", index: 0, input: "12a34", groups: undefined]
reg.exec(string);
// ["12", index: 0, input: "12a34", groups: undefined]
可以看到,由于没有设置/g或/y,因此第二次的匹配没有接着上次的位置继续匹配,而是再次匹配到了字符串12
。
但是在设置了/g或/y后,正则表达式就会变成有状态的,它上次的匹配位置会被记录在lastIndex
属性中,此时,再次执行exec
时,便可以从上次匹配到的位置继续向后匹配:
let reg = /\d+/g;
let string = '12a34';
reg.lastIndex; // 0
reg.exec(string);
// ["12", index: 0, input: "12a34", groups: undefined]
reg.lastIndex; // 2,本次的初始匹配位置
reg.exec(string);
// ["34", index: 3, input: "12a34", groups: undefined]
可以看到,设置了/g后,正则表达式在执行了一次exec
后,lastIndex
的值变成了2,这意味着下次执行exec
时,将从索引位置2开始继续匹配,因此这次匹配到了字符串34
。
因此,一旦设置了/g,那么连续多次执行exec,就可以将字符串中所有符合条件的子串匹配出来。如:
let reg = /\d+/g;
let string = '12a34f56h';
cosnole.log(reg.exec(string));
// ["12", index: 0, input: "12a34f56h", groups: undefined]
while(reg.lastIndex !== 0){
console.log(reg.exec(string));
}
// ["34", index: 3, input: "12a34f56h", groups: undefined]
// ["56", index: 6, input: "12a34f56h", groups: undefined]
// null
由于在匹配失败时,lastIndex会被自动置为0,因此判断lastIndex是否为0可以判定匹配是否结束。如果没有结束将一直向后匹配,最终输出所有匹配结果。
2. 字符串的正则表达式方法
字符串有两个常用的正则表达式方法:match和replace。
(1). match
该方法非常类似于正则表达式的原型方法exec,但是它并没有exec那么灵活。
两者的关键差异在于,当设置了/g时,exec每次只捕获一个子串,并且会输出完整的捕获组;而match一次会匹配所有符合条件的子串,这样的子串只有一个时,它的输出结果与exec完全一致,但是当符合条件的子串超过1个时,它只会返回匹配结果,不会返回捕获组。如:
let reg = /(\d+)[a-z]/g;
let string = '12a34f56g';
reg.exec(string); // ["12a", "12", index: 0, input: "12a34f56", groups: undefined]
reg.exec(string); // ["34f", "34", index: 3, input: "12a34f56", groups: undefined]
reg.exec(string); // ["56g", "56", index: 6, input: "12a34f56", groups: undefined]
string.match(reg);
// ["12a", "34f", "56g"]
// 假如这里只能匹配到一个结果,那么它也会像exec一样输出捕获组
可以看到,每次执行exec都会执行一次匹配,输出匹配结果和捕获组。但是使用match方法却只输出了匹配结果,捕获组并没有输出出来。因此,如果捕获组的值对你很重要,请不要选择match方法。
(2). replace
这是非常常用的子串替换方法。我们最简单的用法一般就是直接把一个字符串替换成另一个字符串,如:
'abc'.replace('b', 'm'); // amc
但是当我们把replace方法与正则表达式结合时,它的作用可能会有点超出想象。
replace的第一个参数可以传入一个正则表达式,此时第二个参数除了可以是一个字符串外,还可以是一个函数。函数接受的参数如下表:
变量名 | 代表的值 |
---|---|
match | 匹配的子串 |
p1,p2, … | 第n个括号匹配的字符串,即第n个捕获组 |
offset | 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 ‘abcd’,匹配到的子字符串是 ‘bc’,那么这个参数将会是 1) |
string | 被匹配的原字符串 |
举个例子,下面的正则表达式匹配a=b 这种结构的子串,并且分别捕获a和b对应的值,我们把a和b对应的值分别记为key和value: |
let reg = /([^=&]+)=([^&]*)/g;
现在我们执行如下的替换:
var result = {};
"foo=1&bar=2&foo=3".replace(/([^=&]+)=([^&]*)/g,
function(match, key, value){
result[key] = ( result[key] ? result[key] + "," : "") + value;
return match;
})
由于设置了/g,因此这里的回调函数会被多次执行,每次传入的是一组匹配结果(与exec方法的行为一致,由于匹配位置和原始字符串我们用不到,因此这里没有传)。
第一次,正则表达式匹配到了子串foo=1
,并且分别捕获了foo
和1
,因此我们的函数前三个值分别是'foo=1'、'foo'、'1'
,分别记为match、key、value
。在回调函数中,我们先检查result[key]
是否存在,如果存在,就把value前加个逗号拼在之前的值后面,否则直接把value赋给该key。随后该回调函数会继续匹配到bar=2
和foo=3
,并重复上述过程。
猜猜最终result的值是什么样的?
result = {
foo: "1,3",
bar: "2"
}
看起来非常巧妙对吧?当你熟练掌握正则表达式后,它可能会远比你现在以为的强大得多!
总结
如果你读了本文,觉得已经完全掌握了正则表达式,那你可太低估正则表达式了。。。
友情链接:正则表达式(应用篇)
更多推荐
所有评论(0)