正则表达式
简介
正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为"元字符")组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
非打印字符
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
特殊字符
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和\ )。
\匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+匹配前面的子表达式一次或多次。要匹配 + 字符,请使用\ +。
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用\ . 。
[ 标记一个中括号表达式的开始。要匹配 [,请使用\ [。
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\' 匹配 "",而 '\(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{ 标记限定符表达式的开始。要匹配 {,请使用 \{。
| 指明两项之间的一个选择。要匹配 |,请使用 \|。
量词和限定符
*
(贪婪)匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+(懒惰)匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
?(占有) 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的 "does" 或 "doxy" 中的 "do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
*?
重复任意次,但尽可能少重复
如 "acbacb" 正则 "a.?b" 只会取到第一个"acb" 原本可以全部取到但加了限定符后,只会匹配尽可能少的字符 ,而"acbacb"最少字符的结果就是"acb"
+? 重复1次或更多次,但尽可能少重复, 与上面一样,只是至少要重复1次
?? 重复0次或1次,但尽可能少重复 如 "aaacb" 正则 "a.??b" 只会取到最后的三个字符"acb"
{n,m}? 重复n到m次,但尽可能少重复,如 "aaaaaaaa" 正则 "a{0,m}" 因为最少是0次所以取到结果为空
{n,}? 重复n次以上,但尽可能少重复,如 "aaaaaaa" 正则 "a{1,}" 最少是1次所以取到结果为 "a
元字符
"^" :^会匹配行或者字符串的起始位置,有时还会匹配整个文档的起始位置。
"$" :$会匹配行或字符串的结尾
"\b" :不会消耗任何字符只匹配单词边界,也就是指单词和空格间的位置,常用于匹配单词边界 如 我想从字符串中"This is Regex"匹配单独的单词 "is" 正则就要写成 "\bis\b
"\d": 匹配数字,
例如要匹配一个固定格式的电话号码以0开头前4位后7位,如0737-5686123 正则:^0\d\d\d-\d\d\d\d\d\d\d$
"\w":匹配字母,数字,下划线.
例如我要匹配"a2345BCD__TTz" 正则:"\w+" 这里的"+"字符为一个量词指重复的次数,等价于'[A-Za-z0-9_]'。
"\W"
匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
"\s":匹配空格
例如字符 "a b c" 正则:"\w\s\w\s\w" 一个字符后跟一个空格,如有字符间有多个空格直接把"\s" 写成 "\s+" 让空格重复
".":匹配除了换行符以外的任何字符
这个算是"\w"的加强版了"\w"不能匹配 空格 如果把字符串加上空格用"\w"就受限了,看下用 "."是如何匹配字符"a23 4 5 B C D__TTz" 正则:".+"
"[abc]": 字符组 匹配包含括号内元素的字符
这个比较简单了只匹配括号内存在的字符,还可以写成[a-z]匹配a至z的所以字母就等于可以用来控制只能输入英文了
"[^xyz]"
负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'
捕获组和反向引用
什么是捕获组
捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。当然,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部。
捕获组有两种形式,一种是普通捕获组,另一种是命名捕获组,通常所说的捕获组指的是普通捕获组。语法如下:
普通捕获组:(Expression)
命名捕获组:(?<name>Expression)
什么是反向引用
反向引用匹配原理
捕获组(Expression)在匹配成功时,会将子表达式匹配到的内容,保存到内存中一个以数字编号的组里,可以简单的认为是对一个局部变量进行了赋值,这时就可以通过反向引用方式,引用这个局部变量的值。一个捕获组(Expression)在匹配成功之前,它的内容可以是不确定的,一旦匹配成功,它的内容就确定了,反向引用的内容也就是确定的了。
反向引用必然要与捕获组一同使用的,如果没有捕获组,而使用了反向引用的语法,不同语言的处理方式不一致,有的语言会抛异常,有的语言会当作普通的转义处理。
一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 \n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。可以使用非捕获元字符 ?:、?= 或 ?! 来重写捕获,忽略对相关匹配的保存
其中 ?: 是非捕获元之一,还有两个非捕获元是 ?= 和 ?!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。
简单例子
源字符串:abcdebbcde
正则表达式:([ab])\1
对于正则表达式“([ab])\1”,捕获组中的子表达式“[ab]”虽然可以匹配“a”或者“b”,但是捕获组一旦匹配成功,反向引用的内容也就确定了。如果捕获组匹配到“a”,那么反向引用也就只能匹配“a”,同理,如果捕获组匹配到的是“b”,那么反向引用也就只能匹配“b”。由于后面反向引用“\1”的限制,要求必须是两个相同的字符,在这里也就是“aa”或者“bb”才能匹配成功
考察一下这个正则表达式的匹配过程,在位置0处,由“([ab])”匹配“a”成功,将捕获的内容保存在编号为1的组中,然后把控制权交给“\1”,由于此时捕获组已记录了捕获内容为“a”,“\1”也就确定只有匹配到“a”才能匹配成功,这里显然不满足,“\1”匹配失败,由于没有可供回溯的状态,整个表达式在位置0处匹配失败。
正则引擎向前传动,在位置5之前,“([ab])”一直匹配失败。传动到位置5处时,,“([ab])”匹配到“b”,匹配成功,将捕获的内容保存在编号为1的组中,然后把控制权交给“\1”,由于此时捕获组已记录了捕获内容为“b”,“\1”也就确定只有匹配到“b”才能匹配成功,满足条件,“\1”匹配成功,整个表达式匹配成功,匹配结果为“bb”,匹配开始位置为5,结束位置为7。
扩展一下,正则表达式“([a-z])\1{2}”也就表达连续三个相同的小写字母。
复杂例子
源字符串:aaa bbbb ffffff 999999999
正则表达式:(\w)((?=\1\1\1)(\1))+
正则表达式(\w)((?=\1\1\1)(\1))+从匹配结果上分析,其实就等价于 (\w)(\1)*(?=\1\1\1)(\1),因为“+”等价于“{1,}”,表示至少匹配1次,如果最后一个“((?=\1\1\1)(\1))”匹配成功,那么中间的“((?=\1\1\1)(\1))”一定可以匹配成功,所以中间的限制条件(?=\1\1\1)就没有意义了,这时就可以简写为“(\1)”
可以归纳为等价于
(\w)(\1)*((?=\1\1\1)(\1))
因为“((?=\1\1\1)(\1))”开始和结尾的()原来是用作量词+修饰范围的,这里已经没有什么意义了,所以表达式最后可以归纳为等价于
(\w)(\1)*(?=\1\1\1)(\1)
分析这个表达式就容易多了。“(\w)”匹配一个字符,占一位,“\1”是对“\w”匹配内容的引用,“(\1)*”可以匹配0到无穷多个“(\w)”匹配到的字符,“(?=\1\1\1)(\1)”只占一位,但是“(?=\1\1\1)”要求所在位置右侧有三个连续相同的“(\w)”匹配到的字符,所以在“(?=\1\1\1)”这个位置右侧应该有三个字符,不过只有这个位置右侧的一个字符计入最后的匹配结果,最后两个只作为限制条件,不计入最后的匹配结果 。
以“999999999”为例,第一个“9”由“(\w)”匹配,第二到第六个“9”由“(\1)*”来匹配,第七个“9”由“(?=\1\1\1)(\1)”中最后的“(\1)”来匹配,而第七、八、九这三个“9”是用来保证满足“(?=\1\1\1)”这个条件的。
常见应用软件和编程语言对正则表达式的支持汇总如下:
A.1 grep
grep是一种用来对文件或标准输入文本进行文字搜索的Unix工具,支持基本、扩展和Perl正则表达式。
-E:使用扩展正则表达式
-G:使用基本正则表达式
-P:使用Perl正则表达式
注意事项:
①默认情况下,grep将把包含着匹配的各个文本全部显示,如果只想查看匹配结果,请使用-o选项;
②使用-v选项将对整个操作匹配操作进行求非————只显示不匹配的文本行;
③使用-c选项只显示匹配的总数而不是此次匹配的细节;
④grep工具只能进行搜索操作,不能进行替换操作;
A.2 javascript
javascript 1.x版本在string和RegExp对象的以下几个方法里实现了正则表达式处理。
exec:一个用来搜索一个匹配的RegExp方法
match:一个用来匹配一个字符串的string方法
replace:一个用来完成替换操作的string方法
search:一个用来测试在某给定字符串里是否存在着一个匹配的string方法
split:一个用来把一个字符串拆分为多个子串的string方法
test:一个用来测试在某给定字符串里是否存在着一个匹配的RegExp方法
△JavaScript度对正则表达式的支持源自Perl语言。
注意事项:
①JavaScript使用命令行选项来管理全局的区分大小写搜索,g选项激活全局搜索功能,i选项让匹配操作不区分字母大小写,这两个选项可组合为gi;
②其他命令行选项包括:m,支持多行字符串;s,支持单行字符串;x,忽略正则表达式模式里的空白字符;
③在使用回溯引用时,'将返回被匹配字符串前面的所有东西,`将返回被匹配字符串后面的所有东西;+将返回最后一个被匹配的子表达式,&将返回被匹配到的所有东西
④JavaScript提供一个名为RegExp的全局对象,在执行完一个正则表达式后,可以通过这个对象获得与这次执行有关的信息;
⑤JavaScript不支持POSIX字符类;
⑥JavaScript不支持\A和\Z。
A.3 MySQL
MySQL对正则表达式的支持体现在允许在where字句里使用如下格式的表达式:
REGEXP "erpression"
下面是一条使用了正则表达式的MySQL语句的完整语法:
select * from table where REGEXP 'pattern';
MySQL正则表达式支持很有用,功能也很强大,但它还算不上是一个完备的正则表达式表现;以下是它的不足之处:
①只提供搜索支持,不支持使用正则表达式进行替换操作;
②默认情况下,正则表达式搜索不区分字母的大小写;如果需要区分大小写,必须再增加一个BINARY关键字(放在REGEXP和模式之间);
③用[[:<:]]来匹配一个单词的开头,用[[:>:]]来匹配一个单词的结束;
④不支持向前预测;
⑤不支持嵌入条件;
⑥不支持八进制字符搜素;
⑦不支持\a、\b、\e、\f和\v;
⑧不支持回溯引用;
A.4 Perl
Perl可以说是各种正则表达式的“祖宗”,各种实现几乎斗鱼Perl兼容,正则表达式是Perl的核心组件之一,如果需要在Perl脚本里使用正则表达式,只需如下
给出一个操作和相应模式即可:
①m/pattern:匹配给定的模式;
②s/pattern/pattern:执行一个替换操作;
③qr/pattern:返回一个Regex对象供今后使用;
④sqlit():把一个字符串拆分为子字符串;
注意事项:
①允许把限定字符放在模式的后面。\i用来表明搜索时不区分字母大小写,\g用来表明进行全局搜索;
②使用“回溯引用”时,'将返回被匹配字符串前面的所有东西,`将返回被匹配字符串后面的所有东西;+将返回最后一个被匹配的子表达式,&将返回被匹配到的所有东西
A.5 Sun Java
Java对正则表达式的支持是从1.4版本开始的,此前的JRE(Java Runtime Environment,Java运行环境)不支持正则表达式。
Java语言中的正则表达式匹配功能主要通过java.util.regex.Matcher类和以下方法实现:
find():在一个字符串里寻找一个给定模式的匹配;
lookingAt():用一个给定的模式去尝试匹配一个字符串的开头;
matches():用一个给定的模式去尝试匹配一个完整的字符串;
replaceALL():进行替换操作,对所有的匹配都进行替换;
replaceFirst():进行替换操作,只对第一个匹配进行替换;
Matcher类还提供了几个能让程序员对特定操作做出更细致调控的方法;此外,java.util.regex.pattern类也提供了几个简单易用的包装器方法:
compile():把一个正则表达式编译成一个模式;
flags():返回某给定模式的匹配标志;
matches():在功能上等价于刚才介绍的matches()方法;
pattern():把一个模式还原为一个正则表达式;
sqlit():把一个字符串拆分为子字符串;
Sun发布的Java正则表达式支持与Perl语言基本兼容,但请注意一下几项注意事项:
①要想使用正则表达式,必须先用import java.util.regex.*语句导入正则表达式组件;
②不支持嵌入条件;
③不支持使用\E、\L、\l、\U和\u进行字母大小写转换;
④不支持使用[\b]匹配退格符;
⑤不支持\z;
总结
正则表达式的基本语法都是差不多的,具体不同,我们可以根据查表来,仔细了解,刚刚写正则也遇到很多坑,这个东西写多了就会熟悉,不难需要耐心。