正则表达式(regular expression,简称regEx)已经出现了很多年,对于各种复杂的文本处理工作来说可以说是一种为了强大的武器,而且它可以在几乎所有的程序设计语言和操作系统上使用。正则表达式有两种基本用途:一种是查找特定的信息(搜索),另一种是查找并编辑特定的信息(替换)。
regEx可以说是一种迷你的语言,语法是最容易掌握的部分,真正的挑战是学会如何灵活运用那些语法把实际问题分解为一系列正则表达式并最终解决,它不存在所谓的正则表达式程序,既不是可以直接运行的应用程序,也不是可以下载的软件,但在绝大多数的编程语言和各种开发环境regEx都已被实现。regEx在不同的应用程序/语言种语法和功能往往会有一定的区别,但最基本的核心使用方法和功能大体都是一致的,如果遇到兼容问题可以查阅相关的文档。
§ 匹配单个字符
正则表达式可以包含纯文本(甚至只包含纯文本),同时需要区分大小写,不过大多数regEx的实现都支持不区分大小写的匹配(e.g. Ben 和 ben )。当存在多个匹配结果的时候各种不同的实现也提供了把所有匹配结果全部找出来的机制,通常返回一个数组或者其他专用格式。
静态纯文本的匹配体现不出regEx真正的威力,它可以使用 . 字符(英文句号)去匹配任意一个单个的字符。
. 字符在regEx中有特殊的含义,如果正好需要匹配这个字符呢?就需要想办法告诉表达式你需要的是该字符本身而不是其在表达式里的特殊含义,为此你需要在该字符前面加上一个 \ 字符(反斜杠)对其进行转义。\ 是一个元字符(metacharacter, 表示这个字符有特殊含义,而不是字符本身的含义)。 例如,.a.\.doc 将匹配前三字母中第二个为 a 且以 .doc 结尾的字符串。
正则表达式经常被简称为模式(Pattern),它们其实是一些由字符构成的字符串,这些字符可以是普通字符(纯文本)或元字符(有特殊含义的特殊字符)。. 可以匹配任何字符,\ 用来对字符进行转义,在正则表达式里有特殊含义的字符序列总是以\ 字符开头。
§ 匹配一组字符
如果你只想匹配n 和s 两个字符,在正则表达式中可以使用元字符[ 和] 来定义一个字符集合。在使用[ 和] 定义的字符集合里,这两个字符之间的所有字符都是该集合的组成部分,字符集合的匹配结果是能够与该集合里的任意**一个**成员相匹配的文本。例如,[ns]a 将匹配na 和sa ,这也可以用来同时匹配大小写字母。
当你需要匹配0到9的数字的时候可以使用[0123456789] ,但如果要匹配的是某一个字母呢?不可能将所有的字母都列出来,此时就会频繁用到一些字符区间如[0-9] 、[a-z] 、[A-Z] 等等。例如,[a-f0-7] 等价于[abcdef01234567] ,某一个RGB色值可以是#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f] 。
字符集合通常用来指定一组必须匹配其中之一的字符。但在某一些场合,我们需要反过来做,给出一组不需要得到的字符。即_除了字符集合里的字符,其他字符都可以匹配_。用元字符^ 来表明你相对严格字符集合进行取非匹配,这与逻辑非运算很相似,只是这里的操作数是字符集合。例如,[ns]a[^0-9] 将不匹配结尾为数字的字符串。
元字符[ 和] 用来定义一个字符的集合,其含义是必须匹配该集合里的字符之一。定义一个字符集合的具体做法有两种:一是把所有的字符都列举出来;二是利用元字符- 以字符全歼的方式给出。字符集合可以用元字符^ 来求非。这将把给定的字符集合内所有字符和字符区间强行排除在匹配操作以外,即除了该字符集合里的字符,其他字符都将被匹配。
§ 使用元字符
元字符是一些在正则表达式中有着特殊含义的字符,前面介绍用到的英文句号. 和左右方括号[ ] 都是元字符。因为元字符在表达式里的特殊含义,所以这些字符就无法用来代表他们本身。我们通过在元字符前面加上一个反斜杠\ 来对相关的字符进行转义。例如,我们要匹配数组标识Array[0-9] ,那么正则表达式为Arrary\[[0-9]\] 。同时,由于对元字符进行转义需要用到反斜杠,这意味着反斜杠也是一个元字符,所以在匹配\ 的时候请一定记住使用\\ 。
在进行正则表达式搜索的时候,我们经常雨打需要对原始文本里面的非打印空白字符进行匹配的情况。例如我们可能要把所有的制表符或者换行符找出来,这一类的字符很难直接输入到正则表达式里面,所以需要使用下面列出的特殊元字符来进行输入。
[\b] :回退(并删除)一个字符(Backspace键)
\f :换页符
\b :换行符
\r :回车符
\t :制表符(Tab键)
\v :垂直制表符
TIPS:\r\n 是Windows所使用的文本行结束标签,而Linux和Unix系统只是用一个换行符\n 来结束一个文本行。
字符集合是最常见的匹配形式,而一些常见的字符集合可以使用特殊元字符来替代,它们并不是必不可少的东西,但是使用它们构造的正则表达式简明易懂,在实践中很有用。
匹配数字(与非数字):\d 匹配任何一个数字字符(等价于[0-9] ); \D 匹配任何一个非数字(等价于[^0-9] )。
匹配字母和数字(与非字母和数字):\w 匹配任何一个字母数字字符(大小写均可)或下划线(等价于[a-zA-Z0-9_] );\W 匹配任何一个非字母数字或非下划线字符(等价于[^a-zA-Z0-9_] )。
匹配空白字符(与非空白字符):\s 匹配任何一个空白字符(等价于[\f\n\r\t\v] );\S 匹配任何一个非空白字符(等价于[^\f\n\r\t\v] )。
匹配十六进制或者八进制数值:在正则表达式中,十六进制数值要用前缀\x 来给出。比如说,\x0A 对应于ASCII字符10 (换行符),其效果等价于\n 。八进制数值则要用前缀\0 来给出,数值本身可以是两位或者三位数字。比如说,\011 对应于ASCII字符9 (制表符),等价于\t 。
POSIX字符类是许多(但不是所有)正则表达式实现都支持的一种简写形式,更多支持的字符类可以在搜索引擎中查找到。例如,[:alpha:] 匹配任何一个字母(等价于[a-zA-Z] );[:xdigit:] 匹配任何一个十六进制数字(等价于[a-fA-F0-9] )。
元字符本身的匹配需要使用到转义字符\ ,同时可以使用一些特殊的元字符来匹配一个字符集合或字符类(数字、字母数字字符等等),这些简短的元字符和POSIX字符类可以用来简化正则表达式模式。
§ 重复匹配
匹配一个或多个字符:元字符+ 匹配在其前面的字符或者模式一个或多次(至少一次,不匹配零次的情况)。例如,匹配abc@abc.def.com ,可以使用\w@[\w.]+\.\w+ 。在字符集合中元字符将被解释为普通字符,但转移了也没有坏处,此外匹配字符本身需要转义。
匹配零个或多个字符:元字符* 匹配在其前面的字符或者模式零次或多次,用法同上。
匹配零个或一个字符:元字符? 匹配在其前面的字符或者模式零次或一次,用法同上。
为重复匹配次数设定一个精确的值:正则表达式语言提供了一个用来设定_重复次数_(interval)的语法,重复次数用{ 和} 字符括起来给出。如果你想为重复匹配次数设定一个精确的值,把那个数字写在两个字符之间即可。例如,匹配一个RGB色值可以使用如下模式[0-9A-Fa-f]{6} ,这样即可匹配6位的色值。
为重复匹配次数设定一个区间:这样的语法还可以用来给匹配次数设定一个区间,即设定一个最小值和一个最大值。例如,{2,4} 的含义是最少重复2次、最多重复4次。
匹配“至少重复多少次”:这种情况下只需给出一个最小值但不必给出最大值。例如,{3,} 表示至少重复3次,并不设上限,即必须重复3次或者更多。
这里介绍的. 、+ 和{n,} 在重复次数上面没有上限值,而这样做有时会导致过度匹配现象。因为. 和+ 都是所谓的“贪婪型”元字符,他们在进行匹配时的行为模式是多多益善而不是适可而止。在不需要这些“贪婪行为”的时候,只需要在相应贪婪型元字符后面加上一个? 后缀即可。在这里“懒惰”意味着匹配尽可能少的字符,与之相反的“贪婪”则匹配尽可能多的字符。
§ 位置匹配
这是最常用的边界,由限定符\b 指定单词边界,用它来匹配单词的开始或结尾。这个位置其实就是位于一个能够构成单词的字符和一个不能用来构成单词的字符之间。另外\B-\B 可以匹配一个前后都不是单词边界的连字符即前后都有多余空格的连字符。
用来定义字符串边界的元字符有两个:一个是用来定义字符串开头的^ ,另一个是用来定义字符串结尾的$ 。^ 是几个有着多种用途的元字符之一,只有当它出现在一个字符集合里并金更在左方括号后面是,它才能发挥“求非”作用,如果实在一个字符集合的外面并位于一个模式的开头,它将匹配字符串的开头。出了位置上的差异,$ 的用法与^ 完全一致。例如,在一份web页面中,</html> 之后应该不再有任何实际内容,可以用这个模式来匹配</[Hh][Tt][Mm][Ll]>\s*$ 。
?m 记号十一个能够改变其他元字符行为的元字符序列来启用分行匹配模式(multiline mode),例如,匹配代码中单独为一行以// 开头的代码注释,可以而使用(?m)^\s*//.*$ 模式。
§ 使用子表达式
子表达式是一个更大的表达式的一部分;把一个表达式划分一系列子表达式的目的是为了把那些子表达式当做一个独立元素来使用。子表达式必须用( 和) 括起来。
子表达式允许嵌套包括多层嵌套,子表达式常见的用途包括:对重复次数元字符的作用对象做出精确的设定和控制、对| 操作符的OR条件做出准确的定义等。
§ 回溯引用:前后一致匹配
回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式。使用\1\2\3代表回溯引用第123个表达式(不同环境符号可能不同)。Java中Matcher.group(0)对应着第一个匹配。回溯引用只能用来引用模式里的子表达式。回溯引用匹配通常从1开始计数,第0个匹配代表整个正则表达式。例如,JS中可以有如下用法<[Hh]([1-6])>.*?</[Hh]\1> 来匹配标题,在JS中使用反斜杠来表示回溯引用,不同的实现中差异较大,需查阅相关资料。
回溯引用在文本匹配和文本替换操作里非常有用,同时大小写转换也是类似的思路:
\E :(结束\L 或\U 转换)
\l :(把下一字符转小写)
\L :(把\L 到\E 间字符全部转小写)
\u :(把下一字符转大写)
\U :(把\U 到\E 间字符全部转大写)
§ 前后查找
前后查找(lookaround),对某一位置的前后内容进行查找。其中的前后指模式与被查找文本的相对位置而言,左为前。
向前查找制定了一个必须匹配但不在结果中返回的模式。使用模式(?=) ,向前(左)匹配,返回匹配值不含= 后内容(不消费)。类似的向后匹配是(?<=) ,向后(右)匹配。例如,匹配网页的标题可用(?<=(<[Tt][Ii][Tt][Ll][Ee]>)).*(?=(</[Tt][Ii][Tt][Ll][Ee]>)) 模式。
向前查找和向后查找通常用来匹配文本,其目的是为了确定将被返回为匹配结果的文本位置(通过指定匹配结果的前后必须是那些文本)。称“正向前查找(positive lookahead)”和“正向后查找(positive lookbehind)”。我们还可以对前后查找取非来查找不与给定的模式匹配的文本。
(?=) :正向前查找
(?<=) :正向后查找
(?!) :负向前查找
(?<!) :负向后查找
§ 嵌入条件
正则表达式里条件用? 来定义嵌入条件,嵌入条件不外乎两种情况:①根据一个回溯引用来进行条件处理②根据一个前后查找来进行条件处理。相关语法为(?(regex)true_regex|false_regex) 很像三项表达式,即如果条件成立则执行成立的相关模式,不成立则执行不成立的相关模式。
正则表达式(regular expression,简称regEx)已经出现了很多年,对于各种复杂的文本处理工作来说可以说是一种为了强大的武器,而且它可以在几乎所有的程序设计语言和操作系统上使用。正则表达式有两种基本用途:一种是查找特定的信息(搜索),另一种是查找并编辑特定的信息(替换)。
regEx可以说是一种迷你的语言,语法是最容易掌握的部分,真正的挑战是学会如何灵活运用那些语法把实际问题分解为一系列正则表达式并最终解决,它不存在所谓的正则表达式程序,既不是可以直接运行的应用程序,也不是可以下载的软件,但在绝大多数的编程语言和各种开发环境regEx都已被实现。regEx在不同的应用程序/语言种语法和功能往往会有一定的区别,但最基本的核心使用方法和功能大体都是一致的,如果遇到兼容问题可以查阅相关的文档。
§ 匹配单个字符
正则表达式可以包含纯文本(甚至只包含纯文本),同时需要区分大小写,不过大多数regEx的实现都支持不区分大小写的匹配(e.g.
Ben和ben)。当存在多个匹配结果的时候各种不同的实现也提供了把所有匹配结果全部找出来的机制,通常返回一个数组或者其他专用格式。静态纯文本的匹配体现不出regEx真正的威力,它可以使用
.字符(英文句号)去匹配任意一个单个的字符。.字符在regEx中有特殊的含义,如果正好需要匹配这个字符呢?就需要想办法告诉表达式你需要的是该字符本身而不是其在表达式里的特殊含义,为此你需要在该字符前面加上一个\字符(反斜杠)对其进行转义。\是一个元字符(metacharacter, 表示这个字符有特殊含义,而不是字符本身的含义)。 例如,.a.\.doc将匹配前三字母中第二个为a且以.doc结尾的字符串。正则表达式经常被简称为模式(Pattern),它们其实是一些由字符构成的字符串,这些字符可以是普通字符(纯文本)或元字符(有特殊含义的特殊字符)。
.可以匹配任何字符,\用来对字符进行转义,在正则表达式里有特殊含义的字符序列总是以\字符开头。§ 匹配一组字符
如果你只想匹配
n和s两个字符,在正则表达式中可以使用元字符[和]来定义一个字符集合。在使用[和]定义的字符集合里,这两个字符之间的所有字符都是该集合的组成部分,字符集合的匹配结果是能够与该集合里的任意**一个**成员相匹配的文本。例如,[ns]a将匹配na和sa,这也可以用来同时匹配大小写字母。当你需要匹配0到9的数字的时候可以使用
[0123456789],但如果要匹配的是某一个字母呢?不可能将所有的字母都列出来,此时就会频繁用到一些字符区间如[0-9]、[a-z]、[A-Z]等等。例如,[a-f0-7]等价于[abcdef01234567],某一个RGB色值可以是#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]。字符集合通常用来指定一组必须匹配其中之一的字符。但在某一些场合,我们需要反过来做,给出一组不需要得到的字符。即_除了字符集合里的字符,其他字符都可以匹配_。用元字符
^来表明你相对严格字符集合进行取非匹配,这与逻辑非运算很相似,只是这里的操作数是字符集合。例如,[ns]a[^0-9]将不匹配结尾为数字的字符串。元字符
[和]用来定义一个字符的集合,其含义是必须匹配该集合里的字符之一。定义一个字符集合的具体做法有两种:一是把所有的字符都列举出来;二是利用元字符-以字符全歼的方式给出。字符集合可以用元字符^来求非。这将把给定的字符集合内所有字符和字符区间强行排除在匹配操作以外,即除了该字符集合里的字符,其他字符都将被匹配。§ 使用元字符
元字符是一些在正则表达式中有着特殊含义的字符,前面介绍用到的英文句号
.和左右方括号[]都是元字符。因为元字符在表达式里的特殊含义,所以这些字符就无法用来代表他们本身。我们通过在元字符前面加上一个反斜杠\来对相关的字符进行转义。例如,我们要匹配数组标识Array[0-9],那么正则表达式为Arrary\[[0-9]\]。同时,由于对元字符进行转义需要用到反斜杠,这意味着反斜杠也是一个元字符,所以在匹配\的时候请一定记住使用\\。在进行正则表达式搜索的时候,我们经常雨打需要对原始文本里面的非打印空白字符进行匹配的情况。例如我们可能要把所有的制表符或者换行符找出来,这一类的字符很难直接输入到正则表达式里面,所以需要使用下面列出的特殊元字符来进行输入。
[\b]:回退(并删除)一个字符(Backspace键)\f:换页符\b:换行符\r:回车符\t:制表符(Tab键)\v:垂直制表符TIPS:
\r\n是Windows所使用的文本行结束标签,而Linux和Unix系统只是用一个换行符\n来结束一个文本行。字符集合是最常见的匹配形式,而一些常见的字符集合可以使用特殊元字符来替代,它们并不是必不可少的东西,但是使用它们构造的正则表达式简明易懂,在实践中很有用。
匹配数字(与非数字):
\d匹配任何一个数字字符(等价于[0-9]);\D匹配任何一个非数字(等价于[^0-9])。匹配字母和数字(与非字母和数字):
\w匹配任何一个字母数字字符(大小写均可)或下划线(等价于[a-zA-Z0-9_]);\W匹配任何一个非字母数字或非下划线字符(等价于[^a-zA-Z0-9_])。匹配空白字符(与非空白字符):
\s匹配任何一个空白字符(等价于[\f\n\r\t\v]);\S匹配任何一个非空白字符(等价于[^\f\n\r\t\v])。匹配十六进制或者八进制数值:在正则表达式中,十六进制数值要用前缀
\x来给出。比如说,\x0A对应于ASCII字符10(换行符),其效果等价于\n。八进制数值则要用前缀\0来给出,数值本身可以是两位或者三位数字。比如说,\011对应于ASCII字符9(制表符),等价于\t。POSIX字符类是许多(但不是所有)正则表达式实现都支持的一种简写形式,更多支持的字符类可以在搜索引擎中查找到。例如,
[:alpha:]匹配任何一个字母(等价于[a-zA-Z]);[:xdigit:]匹配任何一个十六进制数字(等价于[a-fA-F0-9])。元字符本身的匹配需要使用到转义字符
\,同时可以使用一些特殊的元字符来匹配一个字符集合或字符类(数字、字母数字字符等等),这些简短的元字符和POSIX字符类可以用来简化正则表达式模式。§ 重复匹配
匹配一个或多个字符:元字符
+匹配在其前面的字符或者模式一个或多次(至少一次,不匹配零次的情况)。例如,匹配abc@abc.def.com,可以使用\w@[\w.]+\.\w+。在字符集合中元字符将被解释为普通字符,但转移了也没有坏处,此外匹配字符本身需要转义。匹配零个或多个字符:元字符
*匹配在其前面的字符或者模式零次或多次,用法同上。匹配零个或一个字符:元字符
?匹配在其前面的字符或者模式零次或一次,用法同上。为重复匹配次数设定一个精确的值:正则表达式语言提供了一个用来设定_重复次数_(interval)的语法,重复次数用
{和}字符括起来给出。如果你想为重复匹配次数设定一个精确的值,把那个数字写在两个字符之间即可。例如,匹配一个RGB色值可以使用如下模式[0-9A-Fa-f]{6},这样即可匹配6位的色值。为重复匹配次数设定一个区间:这样的语法还可以用来给匹配次数设定一个区间,即设定一个最小值和一个最大值。例如,
{2,4}的含义是最少重复2次、最多重复4次。匹配“至少重复多少次”:这种情况下只需给出一个最小值但不必给出最大值。例如,
{3,}表示至少重复3次,并不设上限,即必须重复3次或者更多。这里介绍的
.、+和{n,}在重复次数上面没有上限值,而这样做有时会导致过度匹配现象。因为.和+都是所谓的“贪婪型”元字符,他们在进行匹配时的行为模式是多多益善而不是适可而止。在不需要这些“贪婪行为”的时候,只需要在相应贪婪型元字符后面加上一个?后缀即可。在这里“懒惰”意味着匹配尽可能少的字符,与之相反的“贪婪”则匹配尽可能多的字符。§ 位置匹配
这是最常用的边界,由限定符
\b指定单词边界,用它来匹配单词的开始或结尾。这个位置其实就是位于一个能够构成单词的字符和一个不能用来构成单词的字符之间。另外\B-\B可以匹配一个前后都不是单词边界的连字符即前后都有多余空格的连字符。用来定义字符串边界的元字符有两个:一个是用来定义字符串开头的
^,另一个是用来定义字符串结尾的$。^是几个有着多种用途的元字符之一,只有当它出现在一个字符集合里并金更在左方括号后面是,它才能发挥“求非”作用,如果实在一个字符集合的外面并位于一个模式的开头,它将匹配字符串的开头。出了位置上的差异,$的用法与^完全一致。例如,在一份web页面中,</html>之后应该不再有任何实际内容,可以用这个模式来匹配</[Hh][Tt][Mm][Ll]>\s*$。?m记号十一个能够改变其他元字符行为的元字符序列来启用分行匹配模式(multiline mode),例如,匹配代码中单独为一行以//开头的代码注释,可以而使用(?m)^\s*//.*$模式。§ 使用子表达式
子表达式是一个更大的表达式的一部分;把一个表达式划分一系列子表达式的目的是为了把那些子表达式当做一个独立元素来使用。子表达式必须用
(和)括起来。子表达式允许嵌套包括多层嵌套,子表达式常见的用途包括:对重复次数元字符的作用对象做出精确的设定和控制、对
|操作符的OR条件做出准确的定义等。§ 回溯引用:前后一致匹配
回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式。使用\1\2\3代表回溯引用第123个表达式(不同环境符号可能不同)。Java中Matcher.group(0)对应着第一个匹配。回溯引用只能用来引用模式里的子表达式。回溯引用匹配通常从1开始计数,第0个匹配代表整个正则表达式。例如,JS中可以有如下用法
<[Hh]([1-6])>.*?</[Hh]\1>来匹配标题,在JS中使用反斜杠来表示回溯引用,不同的实现中差异较大,需查阅相关资料。回溯引用在文本匹配和文本替换操作里非常有用,同时大小写转换也是类似的思路:
\E:(结束\L或\U转换)\l:(把下一字符转小写)\L:(把\L到\E间字符全部转小写)\u:(把下一字符转大写)\U:(把\U到\E间字符全部转大写)§ 前后查找
前后查找(lookaround),对某一位置的前后内容进行查找。其中的前后指模式与被查找文本的相对位置而言,左为前。
向前查找制定了一个必须匹配但不在结果中返回的模式。使用模式
(?=),向前(左)匹配,返回匹配值不含=后内容(不消费)。类似的向后匹配是(?<=),向后(右)匹配。例如,匹配网页的标题可用(?<=(<[Tt][Ii][Tt][Ll][Ee]>)).*(?=(</[Tt][Ii][Tt][Ll][Ee]>))模式。向前查找和向后查找通常用来匹配文本,其目的是为了确定将被返回为匹配结果的文本位置(通过指定匹配结果的前后必须是那些文本)。称“正向前查找(positive lookahead)”和“正向后查找(positive lookbehind)”。我们还可以对前后查找取非来查找不与给定的模式匹配的文本。
(?=):正向前查找(?<=):正向后查找(?!):负向前查找(?<!):负向后查找§ 嵌入条件
正则表达式里条件用
?来定义嵌入条件,嵌入条件不外乎两种情况:①根据一个回溯引用来进行条件处理②根据一个前后查找来进行条件处理。相关语法为(?(regex)true_regex|false_regex)很像三项表达式,即如果条件成立则执行成立的相关模式,不成立则执行不成立的相关模式。