在平时的工作中常常会碰到正则,但是我发现,每次都忘记该怎么去写,所以在这里稍微复习总结一下
先看题
/* 题目一 */
var str1 = '123456765464153513566'
// 分割数字每三个以一个逗号划分(从后往前)
// 如1234 -> 1,234
/* 题目二 */
var str2 = "get-element-by-id"
// 将-的命名方式改为小驼峰
// 期望结果getElementById
/* 题目三 */
var str3 = 'getElementById'
console.log(str3.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase())
// 写出运行结果
看完这三个题目,你是否有想法,下面公布答案
// 题目一
console.log(str1.replace(/(\d)(?=(\d{3})+$)/g,'$1,'))
// 123,456,765,464,153,513,566
// 题目二
console.log(str2.replace(/-\w/g, ($0) => {
return $0.slice(1).toUpperCase()
}))
// getElementById
// 题目三
// get-element-by-id
如果你的答案全部正确,那么请忽略下面的内容
复习
简单的匹配规则
- 如想在
apple
这个单词里找到a
这个字符,就直接用/a/
这个正则就可以了 - 如果想匹配
*
,需要使用\
来转义去掉其本来的含义,正则就可以写成/\*/
- 有一些字符本不是特殊字符,使用转义符号就会让它拥有特殊的含义
特殊字符 | 正则表达式 | 记忆方式 |
---|---|---|
换行符 | n | new line |
换页符 | f | form feed |
回车符 | r | return |
空白符 | s | space |
制表符 | t | tab |
垂直制表符 | v | vertical tab |
回退符 | [b] | backspace,之所以使用[]符号是避免和b重复 |
匹配多字符
- 集合的定义方式是使用中括号
[
和]
,如/[123]/
这个正则就能同时匹配1,2,3三个字符。用/[0-9]/
就能匹配所有的数字,/[a-z]/
则可以匹配所有的英文小写字母 - 同时匹配多个字符的简便正则表达式:
匹配区间 | 正则表达式 | 记忆方式 |
---|---|---|
除了换行符之外的任何字符 | . | 句号,除了句子结束符 |
单个数字, [0-9] | d | digit |
除了[0-9] | D | not digit |
包括下划线在内的单个字符,[A-Za-z0-9_] | w | word |
非单字字符 | W | not word |
匹配空白字符,包括空格、制表符、换页符和换行符 | s | space |
匹配非空白字符 | S | not space |
重复匹配
- 元字符
?
代表了匹配一个字符或0个字符, 例匹配color
和colour
这两个单词, 就可以写为/colou?r/
- 元字符
*
用来表示匹配0个字符或无数个字符, 例匹配color
和colouuuuuur
就可以写为/colou*r/
- 元字符+适用于要匹配同个字符出现1次或多次的情况。
color
和colour
这两个单词, 若使用/colou+r/
来匹配,就只能匹配到colour
匹配特定的次数, 可以使用元字符
{
和}
用来给重复匹配设置精确的区间范围。如a
我想匹配3次,那么我就使用/a{3}/
这个正则,或者说a
我想匹配至少两次就是用/a{2,}/
这个正则。- {x}: x次
- {min, max}: 介于min次到max次之间
- {min, }: 至少min次
- {0, max}: 至多max次
总结
| 匹配规则 | 元字符 |
| :-----:| :----: |
| 0次或1次 | ? |
| 0次或无数次 | * |
| 1次或无数次 | + |
| 特定次数 | {x}, {min, max} |
位置边界
- 单词边界
\b
, 例The cat scattered his food all over the room.
匹配出所有的单词cat
, 就可以写成/\bcat\b/g
- 字符串边界,元字符
^
用来匹配字符串的开头。而元字符$
用来匹配字符串的末尾。
边界总结:
边界和标志 | 正则表达式 | 记忆方式 |
---|---|---|
单词边界 | b | boundary |
非单词边界 | B | not boundary |
字符串开头 | ^ | - |
字符串结尾 | $ | - |
多行模式 | m标志 | multiple of lines |
忽略大小写 | i标志 | ignore case, case-insensitive |
全局模式 | g标志 | global |
子表达式
分组
所有以 (
和 )
元字符所包含的正则表达式被分为一组,每一个分组都是一个子表达式
,它也是构成高级正则表达式的基础。
回溯引用
回溯引用(backreference)指的是后面部分引用前面已经匹配到的子字符串。你可以把它想象成是变量,回溯引用的语法像\1
, \2
,....,其中 \1
表示引用的第一个子表达式,\2
表示引用的第二个子表达式,以此类推。而 \0
则表示整个表达式。
// 例如
// 匹配下面字符串中两个连续的单词
// Hello what what is the first thing, and I am am scq000.
var str4 = 'Hello what what is the first thing, and I am am scq000.'
console.log(str4.match(/\b(\w+)\s\1/g))
用 $1
, $2
...来引用要被替换的字符串
var str = 'abc abc 123';
str.replace(/(ab)c/g,'$1g');
// 得到结果 'abg abg 123'
如果我们不想子表达式被引用,可以使用非捕获正则(?:regex)这样就可以避免浪费内存。
var str = 'scq000'.
str.replace(/(scq00)(?:0)/, '$1,$2')
// 返回scq00,$2
// 由于使用了非捕获正则,所以第二个引用没有值,这里直接替换为$2
var str4 = 'scq000 scq001'
console.log(str4.replace(/(scq00)(?:0)/, '$1,$2'))
// 返回 scq00,$2 scq001
前向查找
前向查找(lookahead)是用来限制后缀的。凡是以 (?=regex)
包含的子表达式在匹配过程中都会用来限制前面的表达式的匹配。例如happy
happily
这两个单词,我想获得以 happ
开头的副词,那么就可以使用/happ(?=ily)/
来匹配, 就可以匹配到单词happily
的happ
前缀。如果我想过滤所有以 happ
开头的副词,那么也可以采用负前向查找的正则/happ(?!ily)/
,就会匹配到happy
单词的happ
前缀。
后向查找
后向查找(lookbehind)是通过指定一个子表达式,然后从符合这个子表达式的位置出发开始查找符合规则的字串。举个简单的例子: apple
和 people
都包含 ple
这个后缀,那么如果我只想找到 apple
的 ple
,该怎么做呢?我们可以通过限制app这个前缀,就能唯一确定 ple
这个单词了。
var str4 = 'apple people';
console.log(str4.replace(/(?<=ap)ple/,'-'))
// 得到结果 'ap- people'
// 说明匹配到的是单词apple的ple
(?<=regex)
的语法就是后向查找,regex
指代的子表达式会作为限制项进行匹配,匹配到这个子表达式后,就会继续向后查找。另外一种限制匹配是利用(?<!regex)
语法,这里称为负后向查找。与正前向查找不同的是,被指定的子表达式不能被匹配到。于是,在上面的例子中,如果想要查找 apple
的ple
也可以写成/(?<!peo)ple
总结
| 回溯查找 | 正则 |
| :-----:| :----: |
| 引用 | 0,1,2 和 $0, $1, $2 |
| 非捕获组 | (?:) |
| 前向查找 | (?=) |
| 前向负查找 | (?!) |
| 后向查找 | (?<=) |
| 后向负查找 | (?<!) |
逻辑处理
- 在正则里面,默认的正则规则都是与的关系
- 在
[
和]
内部使用的^
表示非的关系 - 子表达式匹配的非关系就要用到前面介绍的前向负查找子表达式
(?!regex)
或后向负查找子表达式(?<!regex)
- 或关系,通常给子表达式进行归类使用。比如,我同时匹配a,b两种情况就可以使用
(a|b)
这样的子表达式。
解析开头的题目
到这里正则差不多已经复习了一遍,我们现在再去看前面的三道题
题目一
/* 题目一 */
var str1 = '123456765464153513566'
// 分割数字每三个以一个逗号划分(从后往前)
// 如1234 -> 1,234
console.log(str1.replace(/(\d)(?=(\d{3})+$)/g,'$1,'))
// 123,456,765,464,153,513,566
解析:\d
表示单个数字,(?=(\d{3})+$)
是一个前向查找,\d{3})+$
表示匹配3位数字一次或者多次并且以三位数字结尾。连在一起看就是,匹配一个数字,数字后面的数字位数是3的倍数,所以匹配到的数字是3, 6, 5, 4, 3, 3
,然后替换为$1,
,故3
替换为3,
、 6
替换为6,
....
题目二
/* 题目二 */
var str2 = "get-element-by-id"
// 将-的命名方式改为小驼峰
// 期望结果getElementById
console.log(str2.replace(/-\w/g, ($0) => {
return $0.slice(1).toUpperCase()
}))
解析:首先/-\w/g
的意思是匹配所有前面是-
的单个字符
,匹配的结果是-e, -b, -i
, 然后取其第二位(也就是将-
截取掉),再转换为大写
题目三
/* 题目三 */
var str3 = 'getElementById'
console.log(str3.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase())
// 写出运行结果
// 答案:get-element-by-id
解析: ([a-z])([A-Z])
的意思就是匹配两个字母,并且第一个是小写,第二个是大写,所以匹配到的结果是tE, tB, yI
,由于()
代表分组,故$1
代表的是匹配到的小写字母,$2
代表的是匹配到的大写字母
参考:正则表达式不要背
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。