在平时的工作中常常会碰到正则,但是我发现,每次都忘记该怎么去写,所以在这里稍微复习总结一下
先看题
/* 题目一 */
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代表的是匹配到的大写字母
参考:正则表达式不要背