JavaScript 正则表达式(实例)
RegExp对象
字面量
var reg=/\bis\b/g;
定义变量reg
然后把正则表达式文本写到两个反斜杠之间,"\正则文本\"
。\b
代表单词边界,\bis\b
代表正则is单纯后面再加个\b
是保证is
是一个完整的单词,而不是两个字符。
var reg=/\bis\b/;
"He is a boy .This is a dog.Where is she?".replace(reg,'IS');
//"He IS a boy .This is a dog.Where is she?"
发现就匹配了第一个单纯is
。
我们使用g
,也就是代表全文搜索匹配。var reg=/\bis\b/g;
var reg=/\bis\b/g;
"He is a boy .This is a dog.Where is she?".replace(reg,'IS');
//"He IS a boy .This IS a dog.Where IS she?"
构造函数
var reg=new RegExp("\\bis\\b","g");
使用new
操作符来调用构造函数,构造函数接收两个参数,第一个字符串里面是正则表达式的文本,这里有些特殊的地方就是反斜线写了两次,是因为Javascript中反斜线本身就是特殊字符,如果我们想使用的话,就要进行转义,然后第二个参数是字符串,也是标志g
也就是代表全文搜索匹配
var reg=new RegExp("\\bis\\b","g");
"He is a boy .This is a dog.Where is she?".replace(reg,'IS');
//"He IS a boy .This IS a dog.Where IS she?"
修饰符
g
:global全文搜索,不添加,搜索到第一个匹配停止
"He is a boy .This is a dog.Where is she?".replace(/\bis\b/g,'IS')
"He IS a boy .This IS a dog.Where IS she?"
i
:ignore case 忽略大小写,默认大小写敏感
"He Is a boy .This is a dog.Where is she?".replace(/\bis\b/gi,'hello')
"He hello a boy .This hello a dog.Where hello she?"
m
:multiple lines 多行搜索
var mulSrt = "@123\n@321\n@876"
mulSrt
"@123
@321
@876 "
mulSrt.replace(/@\d/g, "G")
"G23
G21
G76 "
mulSrt.replace(/^@\d/g, "G")
"G23
@321
@876 "
mulSrt.replace(/^@\d/gm, "G")
"G23
G21
G76 "
元字符
正则表达式由两种基本字符类型组成:
原义文本字符
原义文本字符意思就是代表它本来含义的字符比如/a/
就是想匹配a
"jj abc a b c vf".replace(/a/gi,'hello')
//"jj hellobc hello b c vf"
写/abc/
就是想匹配abc
"jj abc a b c vf".replace(/abc/gi,'hello')
//"jj hello a b c vf"
这些都是原义文本字符。
元字符
元字符实在正则表达式中有特殊含义的非字母字符
比如\b
它匹配的是单词边界,他的意思并不是匹配一个反斜线一个字母b,意思是匹配一个单词边界。这种就是特殊字符。
在正则表达式中有几个字符需要注意他们都是有特殊含义的。
Paste_Image.png字符类
我们工作时希望并不是匹配某个字符,而是希望匹配符合一系列特征abc
。
我们可以使用元字符[]
来构建一个简单的类。
类就是符合某些特征的对象,是指一个泛指,而不是特指某个字符。
比如我们使用表达式[abc]
就把a或者b或者c自定义成为一类,表达式就可以匹配这类的字符。他的意思是有它们一个就行。
"a1b2c3".replace(/[abc]/g,'x')
//"x1x2x3"
这样我们发现abc对应的地方都进行替换了,
字符来取反
使用元字符^
创建反向类/负向类
反向类的意思是不属于某类的内容
表达式[^abc]
表示不是字符a或b或c的内容
"a1b2c3".replace(/[^abc]/g,'x')
//"axbxcx"
把字符a或b或c以为外的字符替换成x了
范围类
正则表达式提供了范围类
我们可以使用[a-z]
来链接两个字符表示从a到z的任意字符
这是一个闭区间,也就是包含a和z本身
"a1b2c3x4z9".replace(/[a-z]/g,'Q');
//"Q1Q2Q3Q4Q9"
会发现a-z字母都被替换
在[]
组成的类内部是可以连写的[a-zA-Z]
"a1b2c3x4z9ASDFGGG".replace(/[a-zA-Z]/g,'Q');
"Q1Q2Q3Q4Q9QQQQQQQ"
匹配所有数字
"2016-11-12".replace(/[0-9]/g,'Q');
"QQQQ-QQ-QQ"
匹配所有数字和横线
"2016-11-12".replace(/[0-9-]/g,'Q');
"QQQQQQQQQQ"
JS预定义类及边界
正则表达式预定义类:
字符 | 含义 |
---|---|
. | 除了回车符和换行符之外的所有字符,等价于[^\r\n] |
\d | 数字字符,等价于[0-9] digit |
\D | 非数字字符,等价于[^0-9] |
\s | 空白符,等价于[\t\n\x0B\f\r] space |
\S | 非空白符,等价于[^\t\b\x0B\f\r] |
\w | 单词字符(字母,数字,下划线),等价于[a-zA-Z_0-9] word |
\W | 非单词字符,等价于[^a-zA-Z_0-9] |
例子:
匹配一个ab+数字+任意字符
的字符串
/ab[0-9][^\r\n]/
"ab12334455sss".replace(/ab[0-9][^\r\n]/g,'Q');
//"Q334455sss"
或者
/ab\d.
"ab3klll".replace(/ab\d./g,"z")
//"zlll"
边界
字符 | 含义 |
---|---|
^ | 以xxx开始 |
$ | 以xxx结束 |
\b | 单词边界 |
\B | 非单词边界 |
\b
单词边界
匹配字符
"This is a boy".replace(/is/g,'0')
//"Th0 0 a boy"
会发现所以 is
都被替换
如果想替换单词,单词前后是有单词边界,所以要写上边界"This is a boy".replace(/\bis\b/g,'0')
"This is a boy".replace(/\bis\b/g,'0')
//"This 0 a boy"
\B
非单词边界
如果想替换This
的is
,观察下特征,发现前面并不是边界,后面是边界那就这样写"This is a boy".replace(/\Bis\b/g,'0')
"This is a boy".replace(/\Bis\b/g,'0')
//"Th0 is a boy"
^
以xxx为开始
"@123@abc@".replace(/@./g,'Q');
//"Q23Qbc@"
匹配全局@和任意字符
"@123@abc@".replace(/^@./g,'Q');
//"Q23@abc@"
以@和任意字符作为开始后面的不匹配
$
以xxx为结束
"@123@abc@".replace(/.@/g,'Q');
//"@12QabQ"
匹配全局任意字符和@
"@123@abc@".replace(/.@$/g,'Q');
//"@123@abQ"
匹配最后一个任意字符和@
var mulSrt = "@123\n@321\n@876"
mulSrt
"@123
@321
@876 "
mulSrt.replace(/@\d/g, "G")
"G23
G21
G76 "
mulSrt.replace(/^@\d/g, "G")
"G23
@321
@876 "
mulSrt.replace(/^@\d/gm, "G")
"G23
G21
G76 "
量词
字符 | 含义 |
---|---|
? | 出现零次或一次(最多出现一次) |
+ | 出现一次或多次(至少出现一次) |
* | 出现零次或多次(任意次) |
{n} | 出现n次 |
{n,m} | 出现n到m次 |
{n,} | 至少出现n次 |
我们希望匹配一个连续出现20次数字的字符串
/\d\d\d\d\d\d\d\d..../反斜杠加d表示数字表示数字出现20次把他写20遍这样非常麻烦如果出现100次那就要写100遍吗。为了解决这个问题正则表达式引入了量词的概念。
"12341111133333333111111111111".replace(/\d{20}/g,"d")
//"d111111111"
匹配数字两次
"1234".replace(/\d{2}/g,"d")
//"dd"
JS正则贪婪模式与非贪婪模式
贪婪模式
了解了量词我们看这样一个场景。
/\d{3,6}/
数字匹配3到6次,我们用12345678
来执行,我们写的正则表达式是匹配数字3到6次,这个字符串中,即可以满足3次4次5次6次,都是可以满足的,那么正则表达式这时候该怎么处理?
正则表达式在处理的时候会尽可能多的匹配,直到匹配失败。
"12345678".replace(/\d{3,6}/g,'A')
//"A78"
我们看到,他是匹配123456,78没有匹配到,也就是说他是尽可能多的匹配而不是少的匹配。
这就是贪婪模式
非贪婪模式
让正则表达式尽可能少的匹配,也就是说一旦匹配成功匹配不再继续尝试,就是非贪婪模式。
做法很简单,在量词后面加上?
即可。
"12345678".replace(/\d{3,6}?/g,'A')
"AA78"
这个时候的执行结果就是123,456后面两个78没有被匹配到。
分组
匹配字符串Byron连续出现3次的场景。
没有接触量词的时候我们把这个单词写3遍,接触量词之后我们可能会这样写Byron{3}
但是它代表的含义和我们预期的并不是一致的。实际上是n
重复3次,前面不重复,量词是作为紧挨着它的字符,并不是整个单词,否则怎么知道你匹配到哪。
"ByronByronByron".replace(/Byron{3}/g,'A')
//"ByronByronByron"
"ByronnnByronByron".replace(/Byron{3}/g,'A')
//"AByronByron"
那么如何让他匹配整个单词呢?或者是我们圈定的内容,那么就用到分组
使用()
可以达到分组功能,使量词作用于分组。
(Byron){3}
这样写就能达到我们预期效果。整个单词重复了3遍。
比如想匹配一个小写字母加一个数字连续出现3次a1b2c3d4
`a1b2c3d4`.replace(/[a-z]\d{3}/g,"A")
//"a1b2c3d4"
这样写是匹配不到的,因为\d{3}
是找数字出现3次的场景。
我们将他分组即可
`a1b2c3d4`.replace(/([a-z]\d){3}/g,"A")
//"Ad4"
这样就达到效果了。这样量词作用于整个分组。
或
使用|
可以达到或的效果
"ByronCasper".replace(/Byron|Casper/g,"A");
//"AA"
这样两个都被匹配到了。
分组在或中的使用
"ByronsperByrCasper".replace(/Byr(on|Ca)sper/g,"A");
//"AA"
反向引用
比如我们想把2016-11-25
变成11/25/2016
如果只是想把-
替换成/
是很简单的
"2016-11-25".replace(/-/g,'/')
//"2016/11/25"
但是它顺序也变了,
以往的写法
"2016-11-25".replace(/\d{4}-\d{2}-\d{2}/g,'11/25/2016')
//"11/25/2016"
但是需要匹配的值变成了2016,月份可能变成11,我们想替换他本身,替换的内容不再是一个常量,而是一个变量。这个时候该怎么处理?
我们用反向引用,
"2016-11-25".replace(/(\d{4})-(\d{2})-(\d{2})/g,'$2/$3/$3')
"11/25/25"
我们用$
引用分组内内容,我们对分组内容进行捕获,捕获后用$1$2$3
来代替。
"2016-11-25".replace(/(\d{4})-(\d{2})-(\d{2})/g,'$1')
//"2016"
"2016-11-25".replace(/(\d{4})-(\d{2})-(\d{2})/g,'$2')
//"11"
"2016-11-25".replace(/(\d{4})-(\d{2})-(\d{2})/g,'$3')
//"25"
"2016-11-25".replace(/(\d{4})-(\d{2})-(\d{2})/g,'$2/$3/$3')
"11/25/25"
忽略分组
不希望捕获某些分组,只需要在分组内加上?:
就可以。
"abcaok".replace(/(abc).(ok)/g,'$1/$2')
//"abc/ok"
"abcaok".replace(/(?:abc).(ok)/g,'$1/$2')
//"ok/$2"
前瞻
正则表达式的前瞻:
- 正则表达式从文本头部向尾部开始解析,文本尾部方向,成为“前”
- 前瞻 就是在正则表达式匹配到规则的时候,向前检查是否符合断言。比如找到儿子为张三,还得看看他的父亲是否是李四
后顾/后瞻方向相反
注意:JavaScript不支持后顾
匹配单词数字加数字
"a2*3".replace(/\w(\d)/g,"A");
//"A*3"
匹配单词数字,注:这个单词数字后面必须有数字才会匹配,(?=\d)
不能算在匹配内容,这是断言。
"a2*3".replace(/\w(?=\d)/g,"A");
//"A2*3"
写个复杂点的案例
"a2*34v8".replace(/\w(?=\d)/g,"A");
//"A2*A4A8"
a2
*34``v8
这三个符合要求
"a2*34vv".replace(/\w(?=\d)/g,"A");
//"A2*A4vv"
a2
*34
vv,这里只有两个匹配vv
v是对的但是它后面不是数字,所以不成功。
现在改一下比配后面不是数字的
"a2*34vv".replace(/\w(?!\d)/g,"A");
"aA*3AAA"
a2*``34``vv
,2变成A。4vv都符合所以是AAA
js对象属性
描述 | 默认 |
---|---|
g:global是否全文搜索 | 默认false |
i:ignore case是否大小写敏感 | 默认false |
m:multiline多行搜索 | 默认值false |
lastIndex:当前正则表达式匹配内容的最后一个字符的下一个位置(即下一次匹配开始的位置)。 | |
source : 正则表达式的文本字符串。 |
test 和 exec方法
正则表达式本身的两个方法
1.test
RegExp.prototype.test(str)
用于测试字符串参数中是否存在匹配正则表达式模式的字符串
如果存在则返回true,否则返回false
lastIndex 记录当前匹配结果的、最后一个字符的、下一个字符的位置
注意:test()方法在匹配的时候当匹配到一个结果时,会从lastIndex位置开始匹配下一个结果,直到不存在的时候才置为0。因此,当使用全局g属性标识时,当匹配到最后一个结果时,lastIndex值指向不存在的位置,此时再执行test()会返回false。
例子:多次执行test()方法,会在true、false之间循环
(/\w/g).test('a')每次执行都是正确的,但是通过实例化对象,需要很大的开销
test()方法:原意就是测试有没有、能不能匹配上,当使用test原意时,没必要加g
2.exec
RegExp.prototype.exec(str)
使用正则表达式模式对字符串执行搜索,并将更新全局RegExp对象的属性一反映匹配结果
如果没有匹配的文本则返回 null,否则返回一个结果数组:
- index 声明匹配文本的第一个字符位置
- input 存放被检索的字符串 string
非全局调用
调用非全局的RegExp对象的 exec()时,返回数组
第一个元素是与正则表达式相匹配的文本
第二个元素是与RegExpObject的第一个子表达式相匹配的文本(如果有的话)
第三个元素是与RegExp对象的第二个子表达式相匹配的文本(如果有的话),以此类推
字符串对象方法
String.prototype.search(reg)
search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。
语法
stringObject.search(regexp)
参数 | 描述 |
---|---|
regexp | 该参数可以是需要在 stringObject 中检索的子串,也可以是需要检索的 RegExp 对象。注释:要执行忽略大小写的检索,请追加标志 i。 |
返回值
stringObject 中第一个与 regexp 相匹配的子串的起始位置。
注释:如果没有找到任何匹配的子串,则返回 -1。
说明
search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。
"a1b1c1d1".search('1')
//1
"a1b1c1d1".search('10')
//-1
"a1b1c1d1".search(1)
//1
"a1b1c1d1".search(/1/)
//1
String.prototype.match(reg)
match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。
参数 | 描述 |
---|---|
searchvalue | 必需。规定要检索的字符串值。 |
regexp | 必需。规定要匹配的模式的 RegExp 对象。如果该参数不是 RegExp 对象,则需要首先把它传递给 RegExp 构造函数,将其转换为 RegExp 对象。 |
返回值
存放匹配结果的数组。该数组的内容依赖于 regexp 是否具有全局标志 g。
说明
match() 方法将检索字符串 stringObject,以找到一个或多个与 regexp 匹配的文本。这个方法的行为在很大程度上有赖于 regexp 是否具有标志 g。
如果 regexp 没有标志 g,那么 match() 方法就只能在 stringObject
中执行一次匹配。如果没有找到任何匹配的文本, match() 将返回
null。否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。该数组的第 0
个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本。除了这些常规的数组元素之外,返回的数组还含有两个对象属性。index
属性声明的是匹配文本的起始字符在 stringObject 中的位置,input 属性声明的是对 stringObject 的引用。
如果 regexp 具有标志 g,则 match() 方法将执行全局检索,找到 stringObject
中的所有匹配子字符串。若没有找到任何匹配的子串,则返回
null。如果找到了一个或多个匹配子串,则返回一个数组。不过全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的是
stringObject 中所有的匹配子串,而且也没有 index 属性或 input 属性。
注意:在全局检索模式下,match() 即不提供与子表达式匹配的文本的信息,也不声明每个匹配子串的位置。如果您需要这些全局检索的信息,可以使用 RegExp.exec()。
var str="Hello world!"
document.write(str.match("world") + "<br />")
document.write(str.match("World") + "<br />")
document.write(str.match("worlld") + "<br />")
document.write(str.match("world!"))
输出
world
null
null
world!
正则方法非全局匹配
var reg3 = /\d(\w)/;
var reg4 = /\d(\w)/g;
var ts = '$1a2b3c4d5e';
var ret = ts.match(reg3);
console.log(ret); //["1a", "a", index: 1, input: "$1a2b3c4d5e"]
document.write(ret); //1a,a
正则方法全局匹配
var reg3 = /\d(\w)/;
var reg4 = /\d(\w)/g;
var ts = '$1a2b3c4d5e';
var ret = ts.match(reg4);
console.log(ret); //["1a", "2b", "3c", "4d", "5e"]
document.write(ret);//1a,2b,3c,4d,5e
String.prototype.match(reg)方法
split() 方法用于把一个字符串分割成字符串数组。
基本用法
var op = 'a,b,c,d';
var spo = op.split(',');
console.log(spo);
//["a", "b", "c", "d"]
在一些复杂分割情况下可以用正则用法
var op = 'a1b2c3d';
var spo = op.split(/\d/);
console.log(spo);
//["a", "b", "c", "d"]
再复杂
var op = 'a,b|c?d';
var spo = op.split(/[,|\|\?]/g);
console.log(spo);
//["a", "b", "c", "d"]
String.prototype.replace方法
传入两个参数replace(找谁,替换谁)
"a1b".replace(/1/,2)
//"a2b"
"a1b1c1".replace(/1/g,2)
//"a2b2c2"
"a1b1c1".replace(/\d/g,2)
"a2b2c2"
如果我想把"a1b2c3d4" => "a2b3c4d5"该怎么实现呢?
var h = "a1b2c3d4".replace(/\d/g, function(match, index, origin) {
//console.log(index);
return parseInt(match) + 1;
});
console.log(h);//a2b3c4d5
var h = "a1b2c3d4e5".replace(/(\d)(\w)(\d)/g, function(match, g1, g2, g3, index, origin) {
//console.log(index);
return g1 + g3;
});
console.log(h);//a12c34e5