10分钟学完javascript正则表达式
javascript 正则表达式 使用教程
正则表达式(regular expression)是一个描述字符模式的对象。本章的前半部分会梳理一些正则的基础知识,以便后续阅读,后半部分会有正则的扩展和一些练习题目,希望读者可以耐心读完,并动手验证,相信一定会有所收获。
常用方法
RegExp对象包含两个方法:test()和 exec()
String上包含四个方法:match(), replace(), search(), split()
// test()
var reg = /jim/;
reg.test("my name is jim") // true
// exec()
var reg = /jim/;
reg.exec("my name is jim") // ["jim", ...]
// match() 返回和exec类似
var reg = /jim/;
var str = "my name is jim";
str.match(reg) // ["jim", ...]
// replace() 无返回值,修改源字符串
var reg = /jim/;
var str = "my name is jim";
str.replace(reg, "") // "my name is "
// search() 返回一个开始位置
var reg = /jim/;
var str = "my name is jim";
str.search(reg) // 11
// split() 返回拆分后的数组
var reg = /jim/;
var str = "my name is jim";
str.split(reg) // ["my name is ", ""]
修饰符
i修饰符,忽略大小写
// 将正则和字符串中的大小写都忽略
var reg = /Jim/i;
reg.test("my name is jiM") // true
g修饰符,全局匹配
// 在全局匹配模式下的区别
var reg = /jim/ig;
var str = "my name is Jim, is jim";
// exec方法
reg.exec(str) // ["Jim", index: 11, ...] 打印第一个Jim
console.log(reg.lastIndex) // 14
//继续调用此方法
reg.exec(str) // ["jim", index: 19, ...] 打印第二个jim
console.log(reg.lastIndex) // 22
// match方法
str.match(reg) // ["Jim", "jim"]
// replace方法
str.replace(reg, "") // my name is os, is os
RegExp的实例上的属性lastIndex标识下次匹配开始的位置
m修饰符,多行匹配(不常用且IE不支持)
多行模式修饰的是^与$
多行模式开启后则“ . ”号会匹配除了换行符之外的所有字符,不开启则匹配所有字符
var pattern = /^\d+/mg;
var str = '1.jim\n2.tom\n3.rom';
var result = str.replace(pattern, '#');
// "#.jim
// #.tom
// #.rum"
不开启多行匹配
// "#.jim
// 2.tom
// 3.rum"
获取控制
获取控制包括很多分类:
数字字符类(. [a-z0-9] \d \w 等)
空白字符类(\0 \n \r \s 等)
锚字符类(^ $ 等)
重复字符类(? * + {m, n} 等)
替代字符类( one|two|three )
记录字符类( \1-9或$1-9)
锚字符 ^在正则中表示从头匹配,在[]中表示取反
分组 (pattern)
捕获型分组(pattern),自动设置组号
var reg = /(A+)((B|C|D)+)(E+)/ig;//该正则表达式有4个分组
// RegExp.$1 -> (A+)
// RegExp.$2 -> ((B|C|D)+)
// RegExp.$3 -> (B|C|D)
// RegExp.$4 -> (E+)
// 捕获组,将匹配到的分组保存在内存中方便后面引用
var str = "my name is jim";
// 现在需要给jim上色
str.replace(/(jim)/, "$1")
// my name is jim
// 现在需要给语句断句,规则是遇到ing则加上","号
var var str = "adfingsdlfjingsgiinging";
var reg = /(ing)/g
str.replace(reg, "$&,")
// adfing,sdlfjing,sgiing,ing,
// 反向引用
// 捕获组捕获到的内容在正则表达式内部进行引用,则为反向引用
var str = "jim is jim"
var str2 = "jim is tom"
/(\w{3}) is \1/.test(str) // true
/(\w{3}) is \1/.test(str2) // ??
/(\w{3}) is \6/.test(str) //false 越界了按照字符串处理
我们使用的比较多的都是捕获型分组,只有这种分组才会暂存匹配到的串到内存中以供后面使用
$&表示所有匹配的子串, $`表示匹配字串左侧文本, $'表示匹配字串右侧文本
非捕获型 (?:pattern)
有的时候只是为了分组并不需要捕获的情况下就可以使用非捕获型分组:
var reg = /(?:\d{4})-(\d{2})-(\d{2})/
var date = '2012-12-21'
reg.exec(date) // ["2012-12-21", "12", "21"]
RegExp.$1 // 12
RegExp.$2 // 21
非捕获型在没有使用到$分组时的表现和捕获型是一致的
正向前瞻型 (?=pattern)
// 我们可以注意到,在上方的断句例子中,最后多了一个,号,我们修改一下规则
// 添加匹配条件,在ing后至少有一个字符才加上,号
var str = "adfingsdlfjingsgiinging";
var reg = /(ing)(?=\w+)/g
str.replace(reg, "$&,")
"adfing,sdlfjing,sgiing,ing"
// 练习题1 如何匹配文章中所有以ing结尾的单词(不包含ing)?
正向匹配只是做为了一个条件,在匹配结果中并不包含这个条件,如上例的断句,最终的,号是加在了ing的后面,而非条件后方。
反向前瞻型 (?!pattern)
var str = "my name is jim";
var str2 = "my name is tom";
var reg = /my name is (?!jim)/g
reg.test(str) //false
reg.test(str2) //true
// 练习题2 提取html中img元素的src的值?
所谓的正向前瞻和反向前瞻都属于先行断言,先行断言是指,x只有在y前面才匹配,必须写成/x(?=y)/,如:只匹配%号前的的数字应这样写, /\d+(?=%)/, ES5之前js的正则引擎只支持先行断言,ES6新增了后行断言,我们后面会说到。
贪婪惰性
重复字符类的控制分为贪婪和惰性两种模式
贪婪模式
+, ?, *, {}
var str = "abc"
var reg = /\w+/
str.replace(reg, "xxx")
// 输出xxx
贪婪模式下的 “+” 会尽量多的匹配,所以\w+匹配到abc,然后将之替换为xxx
惰性模式
在控制符后添加?号可开启惰性模式
+?, ??, *?, {}?
var str = "abc"
var reg = /\w+?/g
str.replace(reg, "xxx")
// 输出xxxxxxxxx
// 练习题3 提取所有span中的文本?
// var str = "jimtom"
贪婪模式下的 “+?” 会尽量少的匹配,所以\w+?每次只匹配一个字符,然后全局模式下将所有满足匹配的字符都替换为xxx
在非全局模式下输出值为:xxxbc
ES6后 常用正则扩展
新增y(粘连)修饰符
// y修饰符和g修饰符类似 都有全局匹配的功能
var str = "jim,tom";
var reg1 = /\w+/g
var reg2 = /\w+/y
// 第一次执行
reg1.exec(str) // jim
reg2.exec(str) // jim
// 第二次执行
reg1.exec(str) // tom
reg2.exec(str) // null
// 区别就是 y修饰符的后一次匹配会严格的从上次结束的位置匹配
// 而g修饰符会从上一次结束位置后的存在匹配的位置开始。
// 我们修改lastIndex后再看结果如何
reg2.exec(str) // jim
reg2.lastIndex = 4 // 改为从,后开始
reg2.exec(str) // tom
新增s(doAll)修饰符
我们知道获取控制中,".“字符是匹配除换行符以外的所以字符,现在新增了s修饰符使得”."能够匹配所有字符包括换行符
var str = "jim\ntom";
var reg1 = /jim.tom/;
var reg2 = /jim.tom/s;
reg1.test(str) //false
reg2.test(str) //true
后行断言
在前文中我们使用先行断言获取了 %号前面的数字,现在我们使用后行断言获取"$"符后的数字
// 后行肯定断言
var str = "$320, $50";
var reg = /(?<=\$)\d+/g
str.match(reg) //["320", "50"]
// 后行否定断言 匹配不是$符的后方的数字
var str = "$320, $50, 1,12,a123";
var reg = /(?
str.match(reg) // ["20", "0", "1", "12", "123"]
// 问题,此处若是使用惰性匹配结果如何呢?
var reg = /(?
str.match(reg) // ["2", "0", "0", "1", "1", "2", "1", "2", "3"]
后行断言”正好与“先行断言”相反,x只有在y后面才匹配,必须写成/(?<=y)x/
具名组
在ES6中我们可以为分组设置一个名称
var reg = /(?\d{4})-(?\d{2})-(?\d{2})/;
//1 通过正则的result中的分组名获取值
const result = reg.exec('1995-01-01');
const year = result.groups.year; // 1995
const month = result.groups.month; // 01
const day = result.groups.day; // 01
//2 replace中通过 $替换
"1995-05-05".replace(reg, "$/$/$")
// 1995/05/05
//3 通过解构直接提取匹配值给变量
var {groups: {year, month, day}} = reg.exec("1995-05-05")
// year:"1995" month:"05" day:"05"
//4 反向引用
// 在没有具名组时我们通过\1-9在表达式中反向引用
// 具名组中使用 \k引用
var str = "jim is jim"
var reg = /(?\w+)\sis\s\k/
reg.test(str) //true
练习题
如何匹配文章中所有以ing结尾的单词(不包含ing)?
var str = "a, bing, c is gogoing, my name is ing";
var reg = /\b[\w]+(?=ing\b)/g
str.match(reg) // ["b", "gogo"]
提取html中img元素的src的值?
var str = "";
var reg = /]+)\1[^>]*>/i
分段解析:
(?:(?!src=).)* 匹配到不包含src=的任意字符,将src=前面的匹配
(['"]?) :分组一,匹配到单引号或者双引号。
([^'"\s>]+):分组二 匹配主体内容
\1: 反向引用分组一,保证字符串两侧的引号相同
[^>]*: 匹配主体右侧的除>的任意字符
// 运行结果
0: ""
1: "'"
2: "https://www.wddsss.com"
提取所有span中的文本?
// 使用惰性模式匹配内容
var pattern = /(.+?)/g;
var str = 'jimtom';
str.replace(pattern, "$1,");
// jim,tom,
// 思考,若是使用贪婪模式结果如何呢?
// 其他方法
var pattern = /([^]*)/g;