正则表达式总结
正则表达式入门
用途
- 搜索文件中包含的字符
- 在Web网页中生成一些合法的HTML代码
- 检查电子邮件是否符合正确的语法格式
- 替换代码中的字符
- 定向检索包含某字符的文件
- 将数据批量导入应用程序
- 搜索文件中特定文本的位置
根本上来说,正则表达式的两种基本用途:搜索和替换
正则表达式概念
正则表达式是一些用来匹配和处理文本的字符串,这种语言的用途就是为了解决我们前面所描述的种种问题。
JavaScript中的正则表达式
JavaScript 1.x版本在String和RegEx对象的以下几个方法里实现了正则表达式处理。
- exec:一个用来搜索一个匹配的RegEx方法
- match:一个用来匹配一个字符串的String方法
- replace:一个用来完成替换操作的String方法
- search:一个用来测试在某给定字符串里是否存在着一个匹配String方法
- split:一个用来把一个字符串拆分为多个子串的String方法
- test:一个用来测试在某给定字符串里是否存在着一个匹配的RegEx方法
注意:
- JavaScript使用命令行选项来管理全局的区分大小写搜索:g选项激活全局搜索功能,i选项让匹配操作不区分字母的大小写,这两个选项可以组合为gi。
- 其他命令行选项包括:m,支持多行字符串;s,支持单行字符串;x,忽略正则表达式模式里的空白字符。
- 在使用回溯引用的时候,$'将返回被匹配字符串前面的所有东西,$`将返回被匹配字符串后面的所有东西,$+将返回最后一个被匹配的子表达式,$&将返回被匹配到的所有东西。
- JavaScript提供了一个名为RegExp的全局对象,在执行完一个正则表达式之后,你们可以通过这个对象获得与这次执行有关的信息。
- JavaScript不支持POSIX字符类。
- JavaScript不支持\A和\Z。
匹配单个字符
匹配纯文本
正则表达式可以包含纯文本(甚至可以只包含纯文本),所以看起来可能不像是一个正则表达式。当然,像这样使用正则表达式是一种浪费。
多个匹配结果
绝大多数正则表达式引擎的默认行为是只返回第1个匹配结果。
在JavaScript中,可选的为g(global,全局)标志将返回一个包含着所有匹配的结果数组。
字母的大小写问题
正则表达式是区分字母大小写的。
在JavaScript中,可以用i标志来强制执行一次不区分字母大小写的搜索。
匹配任意字符
.
字符可以匹配任何一个单个的字符(但在大多数的正则表达式中,.
不能匹配换行符)。在SQL中相当于_字符。
匹配特殊字符
如果需要匹配.
字符本身而不是它在正则表达式里的特殊含义,必须在.
的前面加上一个\
(反斜杠)字符来对它进行转义。
匹配一组字符
匹配多个字符中的某一个
可以使用元字符[
和]
来定义一个字符集合。在字符集合中,这两个元字符之间的所有字符都是该集合的组成部分,字符集合的匹配结果是能够与该集合里的任意一个成员相匹配的文本。
注意:
- 字符集合中的字符涉及位置匹配问题。
- 字符集合在不需要区分字母大小写的搜索操作中较为常见。最适合用在从全局看需要区分字母大小写,但在某个局部不需要区分字母大小写的搜索操作里。
利用字符集合区间
使用正则表达式的时候,会频繁地用到一些字符区间。为了简化字符区间的定义,正则表达式提供了一个特殊的元字符,字符区间可以用-
(连字符)来定义:
- A-Z:匹配从A到Z的所有大写字母。
- a-z:匹配从a到Z的所有小写字母。
- A-z:匹配从ASCII字符A到ASCII字符z的所有字母。这个模式一般不常用,因为它还包含着[和^等在ASCII字符表里排列在Z和a之间的字符。
- 0-9:数字区间。
注意:
- 在定义一个字符区间的时候,一定要避免让这个区间的尾字符小于它的首字符。
-
是一个特殊的元字符,作为元字符它只能用在[
和]
之间。在字符集合以外的地方,-
只是一个普通字符,只能与-本身相匹配,因此在正则表达式里,-
字符不需要被转义。- 在同一个字符集合里可以给出多个字符区间。
取非匹配
在某些场合,我们需要反过来做,给出一组不需要得到的字符。换句话说,除了那个字符集合中的字符,其他字符都可以匹配。
用元字符^
来表明你想对一个字符集合进行取非匹配,和逻辑非运算很相似。
注意:
^
的效果将作用于给定字符集合里的所有字符或字符区间,而不是仅限于紧跟在^
字符后面的那一个字符或字符区间。
使用元字符
对特殊字符进行转义
任何一个元字符都可以通过给它加上一个反斜杠字符\
作为前缀的办法来转义。
匹配空白字符
空白元字符:
- [\b]:回退(并删除)一个字符
- \f:换页符
- \n:换行符
- \r:回车符
- \t:制表符
- \v:垂直制表符
匹配特定的字符类别
匹配数字(与非数字)
- \d:任何一个数字字符
- \D:任何一个非数字字符
匹配字母和数字(与非字母和数字)
- \w:任何一个字母数字字符(大小写均可)或下划线字符
- \W:任何一个非字母数字或非下划线字符
匹配空白字符(与非空白字符)
- \s:任何一个空白字符(除了\b字符)
- \S:任何一个非空白字符(除了\b字符)
匹配十六进制或八进制数值
使用十六进制:前缀\x来给出
使用八进制:前缀\0来给出
重复匹配
匹配一个或多个字符
要想匹配同一个字符(或字符集合)的多次重复,只要简单地给这个字符(或字符集合)加上一个+
元字符作为后缀即可。
注意:要给一个字符集合加上
+
后缀时,必须把+
放在这个字符集合的外面。
匹配零个或多个字符
想要匹配一个可有可无的字符,需要使用*
元字符来完成,用法和+
完全一样,但可以匹配该字符(或字符集合)连续出现零次或多次的情况。
匹配零个或一个字符
?
只能匹配一个字符(或字符集合)的零次或一次出现,最多不超过一次。
匹配的重复次数
正则表达式里的+
、*
和?
解决了许多问题,但有些问题光靠它们还不够:
+
和*
匹配的字符个数没有上限。我们无法为它们将匹配的字符个数设定一个最大值。+
、*
和?
至少匹配零个或一个字符。我们无法为它们将匹配的字符个数另行设定一个最小值。- 如果只使用
+
和*
,我们无法把它们将匹配的字符个数设定为一个精确的数字。
正则表达式提供了一个用来设定重复次数的语法。重复次数要用{
和}
字符来给出,把数值写在它们之间。
为重复匹配次数设定一个区间
{
}
语法还可以用来为重复匹配次数设定一个区间,也就是为重复匹配次数设定一个最小值和一个最大值。例如区间{2,4}的含义是最少重复2次,最多重复4次。
匹配“至少重复多少次”
{
}
语法的最后一种用法是给出一个最小的重复次数(但不必给出一个最大值)。{
}
的这种用法与我们用来为重复匹配次数设定一个区间的 {
}
语法很相似,只是省略了最大值部分而已。
防止过度匹配
*
和+
元字符在重复匹配次数方面都没有上限值,而这样做有时会导致过度匹配的现象。
因为*
和+
都是所谓的“贪婪型”元字符,它们在进行匹配时的行为模式是多多益善而不是适可而止的,它们会尽可能地从一段文本的开头一直匹配到这段文本的末尾,而不是从这段文本的开头匹配到碰到第一个匹配时为止。
使用“懒惰型”版本可以避免这个问题,写法很简单,只要给贪婪型元字符加上一个?
后缀即可。
位置匹配
在某些场合,只需要对某段文本的特定位置进行匹配,这就引出了位置匹配的概念。
单词边界
使用边界限定符,也就是在正则表达式里用一些特殊的元字符来表明我们想让匹配操作在什么位置(或边界)发生。
第一种边界:由限定符\b
指定的单词边界,\b
用来匹配一个单词的开始或结尾。
注意:
- 如果你想匹配一个完整的单独的单词,就必须在你想要匹配的文本的前后都加上
\b
限定符。 - \b匹配且只匹配一个位置,不匹配任何字符。
如果你想表明不匹配一个单词边界,请使用\B
(例如查找前后都有多余空格的连字符)。
字符串边界
用来定义字符串边界的元字符有两个:一个是用来定义字符串开头的^
,另一个是用来定义字符串结尾的$
。
注意:^是几个有着多种用途的元字符之一。
- 只有当它出现在一个字符集合里并紧跟在左方括号[的后面时,它才能发挥求非的作用。
- 如果是在一个字符集合的外面并位于一个模式的开头,
^
将匹配字符串的开头。
分行匹配模式
用来启用分行匹配模式的(?m)
记号就是一个能够改变其他元字符行为的元字符序列(必须出现在整个模式的最前面)。
分行匹配模式将使得正则表达式引擎把行分隔符当作一个字符串分隔符来对待,在分行匹配模式下,^不仅匹配正常的字符串开头,还将匹配行分隔符(换行符)后面的开始位置(这个位置是不可见的);类似地,$不仅匹配正常的字符串结尾,还讲匹配行分隔符(换行符)后面的结束位置。
警告:有许多正则表达式实现不支持(?m)
。
子表达式
子表达式是一个更大的表达式的一部分:把一个表达式划分为一系列子表达式的目的是为了把那些子表达式当作一个独立元素来使用。
子表达式必须用(
和)
括起来。
子表达式的嵌套
子表达式允许多重嵌套,这种嵌套的层次在理论上没有限制,但在实际工作中还是应该遵循适可而止的原则。
回溯引用:前后一致匹配
用途
某个模式的第二部分对这个模式的第一部分毫无所知,即使匹配到也有可能是不合法的编程语言写法,所以需要借助回溯引用。
回溯引用匹配
回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式。——可以将回溯引用想象成变量。
注意:
- 回溯引用只能用来引用模式里的子表达式。
- 回溯引用匹配通常从1开始计数(\1、\2等)。在许多现实里,第0个匹配(\0)可以用来代表整个正则表达式。
- 子表达式是通过它们的相对位置来引用的。虽然收到普遍的支持,但这种语法存在着一个严重的不足:如果子表达式的想对位置发生了变化,整个模式也许就不能再完成原来的工作,删除或添加子表达式的后果可能更为严重。
- 为了弥补这一不足,一些比较新的正则表达式实现还支持“命名捕获”:给某个子表达式起一个唯一的名字,然后用这个名字(而不是相对位置)来引用这个子表达式,但还没有得到广泛支持,而且已支持的实现具体的语法也极不统一。
回溯引用在替换操作中的应用
同一个子表达式可以被引用任意多次,只要在需要用到它的地方写出它的回溯引用就行了。
在JavaScript中使用$
符号引用。
大小写转换
有些正则表达式实现允许我们使用元字符对字母进行大小写转换。
- \E:结束\L或\U转换
- \l:把下一个字符转换为小写
- \L:把\L到\E之间的字符全部转换为小写
- \u:把下一个字符转换为大写
- \U:把\U到\E之间的字符全部转换为大写
前后查找
前文中正则表达式都是用来匹配文本的,但有时我们还需要使用正则表达式标记我们想要匹配的文本的位置(不仅仅是文本本身)。这就引出了前后查找(对某一位置的前、后内容进行查找)的概念。
注意:JavaScript仅支持向前查找。
向前查找
向前查找指定了一个必须匹配但不在结果中返回的模式。(它必须用在一个子表达式里)
从语法上来看,一个向前查找模式其实就是一个以?=
开头的子表达式,需要匹配的文本跟在=
的后面。
在向前查找里,被匹配的文本不包含在最终返回的匹配结果里,这被称为“不消费”。
注意:向前查找模式
?=
其实和子表达式(
)
匹配的东西是一样的,区别在于匹配到的字符是否出现在最终的匹配结果里。
向后查找
向后查找就是查找出现在被匹配文本之前的字符(但不消费它),向后查找操作符是?<=
。
警告:
- 向前查找模式的长度是可变的,它们可以包含
.
和+
之类的元字符,所以非常灵活。 - 向后查找模式只能是固定长度。
对前后查找取非
前后查找还有一种不太常见的用法叫做负前后查找。负向前查找将向前查找不与给定模式相匹配的文本,负向后查找将向后查找不与给定模式相匹配的文本。
负前后查找使用!
来取非(它将替换掉=
)。
嵌入条件
正则表达式中的条件
正则表达式里的条件要用?
来定义。嵌入条件分为以下两种情况:
- 根据一个回溯引用来进行条件处理。
- 根据一个前后查找来进行条件处理。
回溯引用条件
回溯引用条件只在一个前面的子表达式搜索取得成功的情况下才允许使用一个表达式。
用来定义这种条件的语法是(?(backreference)true-regex)
,其中?
表明这是一个条件,括号里的backreference是一个回溯引用,true-regex是一个只在backreference存在时才会被执行的子表达式。
条件还可以有否则表达式,否则表达式只在给定的回溯引用不存在(也就是条件没有得到满足)时才会被执行。用来定义这种条件的语法是(?(backreference)true-regex | false-regex)
,这个语法接受一个条件和两个将分别在这个条件得到满足和没有得到满足时执行的子表达式。
前后查找条件
前后查找条件只在一个向前查找或向后查找操作取得成功的情况下才允许一个表达式被使用。定义一个前后查找条件的语法与定义一个回溯引用条件的语法大同小异,只需把回溯引用(括号里的回溯引用编号)替换为一个完整的前后查找表达式就行了。