实例化
javascript中RegExp有两种实例化方式:字面量和构造函数
- 字面量:
var reg = /[a-z0-9]*/g
- 构造函数:
var reg = new RegExp('/[a-z0-9]/', 'g' )
参数
可选参数有三个:g
(global 全局匹配)、i
(ignore case 忽略大小写)、m
(multiple lines 多行匹配),先写一个简单的正则表达式来看看具体效果。
-
参数
g
不加g
参数:
字符串中有两个is
单词,不加g
参数只能匹配到第一个。
现在加上g
参数:
加上g
参数后匹配并替换了所有的is
单词,表达式中\b
表示单词边界,即仅仅匹配独立的is
单词,即使字符串中包含is
也不行,看看不加\b
的效果:
包含is
的单词都被替换了。 -
参数
i
不加参数i表示严格区分大小写,效果如下:
可以匹配is
,但是无法匹配Is
。
加上参数i
的效果如下:
加上参数i
后,is
和Is
都可以匹配到。 -
参数
m
具体含义是:将开始和结束字符(^
和$
)视为在多行上工作(也就是,分别匹配每一行的开始和结束(由\n
或\r
分割),而不只是只匹配整个输入字符串的最开始和最末尾处。后面详细讲解。
字符
正则表达式字符分为两类:原义文本字符和元字符
- 原义文本字符是字符串匹配本身,比如
/\bis\b/g
中的i
和s
,就是为了匹配i
和s
- 元字符是有特殊意义的非字母字符,在不同的场景下有不同的含义,包括
. * + ? ^ $ · | \ () [] {}
范围 []
[]
表示一个字符集合,凡是符合规则的字符都会被匹配到。
/[abcd]/g
和/[a-d]/g
都表示全局匹配a、b、c、d中的任意一个字符(闭区间,两个边界都包含),[]
甚至可以包含多个范围。
/[a-d1-8]/g
表示全局匹配a-d或者1-8中的任意单个字符,在这个表达式中-
有了特殊意义,如果只想匹配“-”这个字符,可以写成以下形式:
[]
还可以取反,在[]
内加上^
,即[^]
,意思是匹配该范围外的所有字符
对比这两个结果就可以看出来。
正则表达式中预定了一些常用的范围
字符 | 等价字符 | 含义 |
---|---|---|
. | [^\r\n] | 除回车和换行外的任意字符 |
\d | [0-9] | 数字字符 |
\D | [^0-9] | 非数字字符 |
\s | [\t\n\x0B\f\r] | 空白符 |
\S | [^\t\n\x0B\f\r] | 非空白符 |
\w | [a-zA-Z0-9_] | 单词字符(数字、字母、下划线) |
\W | [^a-zA-Z0-9_] | 非单词字符 |
边界 ^ $
^
表示以xxx开始(在[]
中表示取反),$
表示以xxx结束,前文提到的\b
表示单词边界,\B
表示非单词边界
/A/g
匹配到了两个A,/^A/g
只能匹配开头的A。同理,看看$
效果
后者只匹配到了结尾的A。
再来看看参数中的m
多行匹配
多行匹配
str是一个多行字符串(含有\r\n),用表达式/^This/g
全局匹配却只能匹配到第一个"This",肉眼看到三行,对编译器来说只有一行,此时如果想要匹配三行,就可以加参数m
进行多行匹配。
数量词 * + ? {}
{m,n}
匹配m到n次(闭区间),{m}
匹配m次,{m,}
至少匹配m次*
匹配任意次,等价于{0, }
?
匹配0次或者1次,至多1次,等价于{0,1}
+
匹配1次或者多次,至少1次,等价于{1,}
除此之外,?
还可以指定贪婪模式和非贪婪模式,这里贪婪的意思和运算符的贪婪是一个意思,就是尽可能多的匹配,举个例子
表达式/\d{3,7}/g
理论上可以匹配3-7个数字,默认是贪婪的,所以匹配7个,如果想指定非贪婪模式看下图
表达式/\d{3,6}?/g
中的?
指定非贪婪模式,尽可能少的匹配,所以"123"替换成了"X",“456”也替换成了"X"。
分组 ()
有时想连续匹配某一段字符而不是某一个字符,可能会这样写
本来想把"hellohellohellooo"替换成"Xoo",没想到替换成了"hellohelloX",也就是说/hello{3}/g
是匹配了3个"o",而不是3个"hello",这时便可以使用分组
|
表示“或”,直接看例子
/hello|world/g
表示既可以匹配"hello"也可以匹配"world",如果把|
和()
结合起来会有什么效果
/hel(lo|wo)rld/g
表示既可以匹配"helworld"也可以匹配"hellorld",也就是"lo"和"wo"任取一个。
利用()
实现反向引用,如何把"2018-11-22"替换成"22/11/2018"?前面所有的替换都是把匹配到的字符串替换成固定的内容,在这里却不是。
/(\d{4})-(\d{1,2})-(\d{1,2})/g
把匹配到的字符串分为三组分别是(\d{4})
、(\d{1,2})
和(\d{1,2})
,分组内容可以用$1
、$2
和$3
取到。
前瞻和后顾
注意:正则表达式从文本头部向尾部解析,所以文本尾部方向叫**前**
前瞻意思是匹配到内容后,还要向前检查是否符合断言,后顾同理。比如在写爬虫,解析页面时,想提取<body>hello world</body>
标签中的字符串"hello world",但是不想要标签<body></body>
,这时便可以用到前瞻和后顾。
'<body>helloworld</body>helloworld'.replace(/(?<=<body>)[a-z]*(?=<\/body>)/g, 'X')
上图可以看到,只有<body></body>
之间的字符串被替换了,标签本身和标签外的字符都没有被替换。说明/(?<=<body>)[a-z]*(?=<\/body>)/g
表达式不仅要匹配符合[a-z]*
,还要符合前后的断言。如果需要写非命题的断言,参考下面的例子。
'<body>helloworld</body>helloworld'.replace(/(?<!<body>)[a-z]{9,}(?!<\/body>)/g, 'X')
表达式 | 逻辑非 | |
---|---|---|
前瞻 | (?=assert) | (?!assert) |
后顾 | (?<=assert) | (?<!assert) |