1
在平时的工作中常常会碰到正则,但是我发现,每次都忘记该怎么去写,所以在这里稍微复习总结一下

先看题

/* 题目一 */
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/这个正则就可以了
  • 如果想匹配*,需要使用\来转义去掉其本来的含义,正则就可以写成/\*/
  • 有一些字符本不是特殊字符,使用转义符号就会让它拥有特殊的含义
特殊字符正则表达式记忆方式
换行符nnew line
换页符fform feed
回车符rreturn
空白符sspace
制表符ttab
垂直制表符vvertical tab
回退符[b]backspace,之所以使用[]符号是避免和b重复

匹配多字符

  • 集合的定义方式是使用中括号[],如/[123]/这个正则就能同时匹配1,2,3三个字符。用/[0-9]/就能匹配所有的数字, /[a-z]/则可以匹配所有的英文小写字母
  • 同时匹配多个字符的简便正则表达式:
匹配区间正则表达式记忆方式
除了换行符之外的任何字符.句号,除了句子结束符
单个数字, [0-9]ddigit
除了[0-9]Dnot digit
包括下划线在内的单个字符,[A-Za-z0-9_]wword
非单字字符Wnot word
匹配空白字符,包括空格、制表符、换页符和换行符sspace
匹配非空白字符Snot space

重复匹配

  • 元字符 ? 代表了匹配一个字符或0个字符, 例匹配 colorcolour 这两个单词, 就可以写为 /colou?r/
  • 元字符 * 用来表示匹配0个字符或无数个字符, 例匹配 colorcolouuuuuur 就可以写为 /colou*r/
  • 元字符+适用于要匹配同个字符出现1次或多次的情况。 colorcolour 这两个单词, 若使用 /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
  • 字符串边界,元字符 ^ 用来匹配字符串的开头。而元字符$ 用来匹配字符串的末尾。

边界总结:

边界和标志正则表达式记忆方式
单词边界bboundary
非单词边界Bnot 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)/来匹配, 就可以匹配到单词happilyhapp前缀。如果我想过滤所有以 happ 开头的副词,那么也可以采用负前向查找的正则/happ(?!ily)/,就会匹配到happy单词的happ前缀。

后向查找

后向查找(lookbehind)是通过指定一个子表达式,然后从符合这个子表达式的位置出发开始查找符合规则的字串。举个简单的例子: applepeople 都包含 ple 这个后缀,那么如果我只想找到 appleple ,该怎么做呢?我们可以通过限制app这个前缀,就能唯一确定 ple 这个单词了。

var str4 = 'apple people';
console.log(str4.replace(/(?<=ap)ple/,'-'))
// 得到结果 'ap- people'
// 说明匹配到的是单词apple的ple

(?<=regex) 的语法就是后向查找regex 指代的子表达式会作为限制项进行匹配,匹配到这个子表达式后,就会继续向后查找。另外一种限制匹配是利用(?<!regex) 语法,这里称为负后向查找。与正前向查找不同的是,被指定的子表达式不能被匹配到。于是,在上面的例子中,如果想要查找 appleple 也可以写成/(?<!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代表的是匹配到的大写字母

参考:正则表达式不要背

顾十三goodMan
7 声望0 粉丝