正则表达式语法参考
本节讲述的正则表达式符合 PCRE 标准,本节展示的示例均为 JavaScript 代码。
正则表达式
正则表达式也被称为正规表达式,他提供了一套强大而有效的规则,用于目标字符串的匹配和替换,相比于简单的字符串操作,正则表达式可以完成更加复杂的任务和目标。
PCRE 标准和 POSIX 标准
正则表达式存在 PCRE 和 POSIX 两种标准,前者提供了更多的功能和更为简洁的语法,因而在编程语言中获得了普遍的支持。
正则表达式中的转义字符
在正则表达式中,\
被视为转义字符,他与其后的一个或多个字符组合,用来表示某种特殊的含义,功能或某个字符。这对于上下文中的特殊字符是非常必要的,因为他们需要转义才能表达自身,比如,表示开始位置的^
需要使用\^
表达自身,表示结束位置的$
需要使用\$
表达自身。
转义
想要详细了解转义,你可以查看转义,转义字符,转义序列介绍一节。
使用正则表达式匹配字符
要想使用正则表达式匹配具体的字符,只需直接书写这些字符即可,前提是他们不必转义。比如,正则表达式he
与字符串'a hero'
中的he
匹配。
当然,除了简单匹配,PCRE 还支持以下的字符匹配方式。
- .
.
用于匹配任何一个非换行符和回车符的字符。- \n
\n
用于匹配一个换行符。- \r
\r
用于匹配一个回车符。- \t
\t
用于匹配一个制表符。- \v
\v
用于匹配一个垂直制表符。- \f
\f
用于匹配一个分页符。- \w
\w
用于匹配一个英文字母,数字(0
至9
)或下划线(_
)字符。- \W
\W
与\w
相反,匹配任何一个非英文字母,数字或下划线的字符。- \d
\d
用于匹配一个数字(0
至9
)字符。- \D
\D
与\d
相反,匹配任何一个非数字字符。- \s
\s
用于匹配一个空白字符(包括空格符,换行符,制表符等)。- \S
\S
与\s
相反,匹配任何一个非空白字符。- \xHH
\x
后面跟随一个两位的十六进制数字,用于转义为对应的字符,比如,\x0A
对应了换行符。- \uHHHH
\u
后面跟随一个四位的十六进制数字,用于转义为对应的字符,比如,\u000D
对应了回车符。- \OOO
\
后面跟随一个三位以内的八进制数字,用于转义为对应的字符,前提是之前不存在对应个数的捕获组,比如,\15
对应了回车符,当\15
之前不存在 15 个捕获组时。- \cX
\c
后面跟随一个英文字母,用于匹配Control和对应字母的组合操作,比如,\cJ
表示Control和J
的组合操作,操作产生的效果需要视具体环境而定。
正则表达式\d\d
匹配两个相邻的数字字符,在字符串'他们的年龄为 12,8,13,102'
中,8
只有一个数字字符,因此不符合要求,102
中的10
是两个相邻的数字字符,符合要求,但末尾剩下的2
不再符合。
'他们的年龄为 12,8,13,102'.match(/\d\d/g)
// 匹配结果:['12', '13', '10']
正则表达式.\s.
匹配两个被空白分割的非换行符和回车符的字符。
'你好 吗?\r\n 不好!'.match(/.\s./g)
// 匹配结果:['好 吗']
正则表达式\w\w\w\w
匹配四个字符的英文字母,数字,下划线(或他们之间的任意组合)。
'I like this c__, what ab__t you?'.match(/\w\w\w\w/g)
// 匹配结果:['like', 'this', 'what', 'ab__']
使用正则表达式匹配字符集
在正则表达式中,可以使用[]
来匹配一个属于指定集合的字符,只需要在括号中包含相应字符或转义序列即可。如果希望实现相反的效果,则需要在[]
中的第一个位置加入^
,以表示匹配一个不属于指定集合的字符。
需要特别指出的是,一些在[]
外起到特殊作用的字符,比如.
,$
,+
,?
,*
,在[]
内会表示其原本含义,一些在[]
外可以使用的转义序列,比如\d
,\w
,在[]
内依然有效。
正则表达式li[kv*?]e
匹配前两个字符为li
,第四个字符为e
,第三个字符属于kv*?
之一的字符组合。
'like live li*e libe lie'.match(/li[kv*?]e/g)
// 匹配结果:['like', 'live', 'li*e']
如何使用正则表达式匹配特定范围的字母或数字?
在正则表达式的[]
中,可使用-
来表示某个范围内的英文字母或数字,比如,A-Z
表示大写字母A
到Z
,b-q
表示小写字母b
到q
,0-7
表示数字0
到7
。
当你希望在[]
中表示-
自身时,最好采用\-
进行转义。
正则表达式li[^c-z*]e
匹配前两个字符为li
,第四个字符为e
,第三个字符不属于*
以及c
到z
的字符序列。
'like live li*e libe lie'.match(/li[^c-z*]e/g)
// 匹配结果:['libe']
如何使用正则表达式匹配退格符?
在大部分语言的字符串中,退格符需要使用\b
进行转义,这样的做法在正则表达式中是不可行的,因为\b
具有其他含义,想要匹配退格符可以采用替代方案,比如[\b]
,[]
中的\b
将转义为退格符,而不是像\d
或\w
一样保持[]
内外转义的一致性。
在正则表达式中捕获匹配内容
在正则表达式中,可使用()
生成一个捕获组(Capture Group),括号中需要包含一个子表达式,比如,I (like) cats
。捕获组一般用于提高运算的优先级,字符串的替换,作为被引用的目标。
如果你仅仅把捕获组中的内容视为一个整体,并不准备进行替换和引用,那么可将其转换为(?:)
表示的非捕获组。
如何在正则表达式中引用已匹配内容?
当你在正则表达式中定义一个捕获组之后,即可使用转义序列\NUM
向后引用(Back Reference)该捕获组,其中NUM
为捕获组的序号。捕获组序号一般会从1
开始,并按照左括号出现的顺序依次递增1
,这一点对于计算嵌套捕获组的序号非常有效。
正则表达式([%#])[^%#]*\1
拥有一个捕获组([%#])
,他与字符%
或#
匹配。\1
引用了该捕获组,因此整个表达式将用于匹配被一对%
或#
括住的内容。
'%晚餐好了吗?%#还是出去吃吧#%不错的主意#'.match(/([%#])[^%#]*\1/g)
// 匹配结果:['%晚餐好了吗?%', '#还是出去吃吧#']
下面,我们做出一些调整,将之前的([%#])
改为嵌套捕获组(:([%#]))
,将之前的\1
改为\2
以保持正确的引用。
':%晚餐好了吗?%#还是出去吃吧#'.match(/(:([%#]))[^%#]*\2/g)
// 匹配结果:[':%晚餐好了吗?%']
如何在 JavaScript 的 replace 方法中引用已匹配内容?
JavaScript 的replace
方法支持正则表达式,如果其第二个参数为字符串,你可以在该字符串中使用$
和捕获组序号来引用捕获组。
正则表达式:([%#])([^%#]*)(\1)
匹配以:
开始并被一对%
或#
括住的内容,replace
方法将替换该内容中的英文:
,%
或#
。
'他说:%晚餐好了嗎?%'.replace(/:([%#])([^%#]*)(\1)/g, ':“$2”')
// 替换结果:'他说:“晚餐好了嗎?”'
使用正则表达式匹配位置
在正则表达式中,你可以使用断言来匹配一个具体的位置,断言不会消耗字符,这意味着被用于断言的字符可以继续在正则表达式中使用。
- \b
\b
用于匹配\w
和\W
之间的边界,这对于确定英文单词通常是有效的。- \B
\B
与\b
相反,匹配两个\w
或两个\W
之间的边界。- ^
^
用于匹配字符串的开始位置,如果开启了多行模式,他还会匹配\r
或\n
后面的位置。- $
$
用于匹配字符串的结束位置,如果开启了多行模式,他还会匹配\r
或\n
前面的位置。- (?<=PATTERN)
- 正向后行断言(Positive Lookbehind Assertion)用于匹配一个位置,该位置之前的内容符合子表达式
PATTERN
的要求。 - (?<!PATTERN)
- 负向后行断言(Negative Lookbehind Assertion)用于匹配一个位置,该位置之前的内容不符合子表达式
PATTERN
的要求。 - (?=PATTERN)
- 正向先行断言(Positive Lookahead Assertion)用于匹配一个位置,该位置之后的内容符合子表达式
PATTERN
的要求。 - (?!PATTERN)
- 负向先行断言(Negative Lookahead Assertion)用于匹配一个位置,该位置之后的内容不符合子表达式
PATTERN
的要求。
后行和先行断言应位于正则表达式的开始或结尾位置
正向后行断言和负向后行断言应位于正则表达式或子表达式的开始位置,
正向先行断言和负向先行断言应位于正则表达式或子表达式的结尾位置,
否则表达式可能无法有效工作。
正则表达式\blive\B
用于匹配作为单词开头出现的live
。
'I live here'.match(/\blive\B/g)
// 匹配结果:null
正则表达式[a-zA-Z]+\b(?! 7)
使用了负向先行断言,用于匹配字符串中包含任何非7
版本的产品。
'iPad 7 iPhone 11 iPad 9 Windows 7 iPhone 7'.match(/[a-zA-Z]+\b(?! 7)/g)
// 匹配结果:['iPhone', 'iPad']
在多行模式下,正则表达式(?<=^The\s)\w+
与字符串开始处的apple
,以及\n
开始处的orange
匹配。
'The apple is...\r\nThe orange is...'.match(/(?<=^The\s)\w+/gm)
// 匹配结果:['apple', 'orange']
在正则表达式中设置匹配次数
你可以在正则表达式中使用量词来设置匹配的次数,而不是重复的书写某些内容,量词可被用于表达式中的大部分目标,包括捕获组,字符集等。
- *
*
表示匹配 0 次或更多次。- +
+
表示匹配 1 次或更多次。- ?
?
表示匹配 0 次或 1 次。- {n}
{n}
表示匹配n
(n
大于等于0
)次。- {n,}
{n,}
表示匹配n
(n
大于等于0
)次或更多次。- {n,m}
{n,m}
表示匹配次数可以大于等于n
(n
大于等于0
),小于等于m
(m
大于等于n
)。
正则表达式\bwo{2,3}\b
匹配以w
开头并含有2
到3
个o
的单词。
'wo woo wooo woooo'.match(/\bwo{2,3}\b/g)
// 匹配结果:['woo', 'wooo']
正则表达式(-?foo){2,}
匹配至少含有两个foo
的字符组合。
'foo foo-foo foo-foo-foo'.match(/(-?foo){2,}/g)
// 匹配结果:['foo-foo', 'foo-foo-foo']
如何在正则表达式中尽可能减少匹配次数?
正则表达式中的量词默认为贪婪模式,他会尽可能多的进行匹配,比如,wo*
与字符串'woooooooho'
中的wooooooo
匹配。如果希望尽可能少的匹配,你需要使用非贪婪模式,在相关量词后加上?
,比如,wo*?
与字符串'woooooooho'
中的w
匹配。
匹配多个正则表达式中的任意一个
你可以使用|
列出多个正则表达式,他们会按照从左到右的顺序进行计算,当某个正则表达式匹配成功时,剩余的表达式将被忽略。
正则表达式I (?:love|like) you
中的非捕获组(?:love|like)
,将与love
或like
匹配。
'I love you, I hate you'.match(/I (?:love|like) you/g)
// 匹配结果:['I love you']
正则表达式中的运算优先级
正则表达式中的各种符号,存在以下降序排列的优先级关系。
- \
- 转义字符
\
的优先级最高。 - (),[]
- 表示捕获组,非捕获组,断言的
()
,以及表示字符集的[]
的优先级较高。 - *,+,?,{n},{n,},{n,m}
- 量词具有中等优先级。
- ^,$,字符
- 表示位置的
^
和$
,以及字符的优先级较低。 - |
- 组合多个正则表达式的
|
的优先级最低。
正则表达式Yes\.|No\b.*?\bbecause\b\s+([^.]+)\.
将匹配Yes
或给出原因的No
。
'No, because nothing.Yes.No'.match(/Yes\.|No\b.*?\bbecause\b\s+([^.]+)\./g)
// 匹配结果:['No, because nothing.', 'Yes.']