第二章 匹配单个字符

正则表达式可以包含纯文本(甚至可以是只包含纯文本)。

绝大多数正则表达式引擎默认行为只是返回第1个匹配结果,但是正则有办法实现全部匹配。在JavaScript中,可选是g。

正则默认区分大小写,如果不可分大小写,比如在JavaScript中,使用i。

.字符(英文句号)可以匹配任何一个单个的字符。比如:c.t可以匹配cat或者cot。

正则表达式可以用来匹配包含着字符串内容的模式,匹配的并不总是整个字符串,而是与某个模式相匹配的字符,即使它们只是整个字符串的一部分。

如果需要匹配.字符,可以进行转义,使用\。比如:a.\.xls,可以陪陪an.xls或者as.xls等。\是一个元字符,表示“这个字符有特殊含义,而不是字符本身含义”。

如果需要对\本身进行搜索,使用\\。

 

第三章 匹配一组字符

[和]定义一个字符集合。比如[ns]a\.xls匹配,na.xls和sa.xls,也会匹配xxxna.xls等等。比如匹配大小写:[Rr]eg[Ee]x,这种用的很多。

[0-9]表示数字[0123456789],这完全等价。

又如:

[A-Z],匹配A到Z

[a-z],匹配a到z

[A-z],匹配A到z,这个模式不常用,因为还包含着[和^等字符,一般用[A-Za-z]

[A-Za-z0-9],匹配所有字母和数字

取非匹配使用[^]形式,比如[^0-9],不匹配数字0到9。

 

第四章 使用元字符

要输出[和],也需要转义:

myArray\[0\]

又如:

\\home\\ben\\sales

空白元字符:

[\b],回退(并删除)一个字腹(backspace键)

\f,换页符号

\n,换行符

\r,回车符

\t,制表符(Tab键)

\v,垂直制表符

\r\n是Windows文本结束标签,而Unix和Linux只是用一个换行符结束一个文本。一般来说\r,\n和\t用的比较多。

数字元字符:

\d,任何一个数字字符,等价于[0-9]

\D,任何一个非数字字符,等价于[^0-9]

比如:

myArray\[\d\]等价于myArray\[[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给出。\c前缀来指定控制字符,如\cZ匹配ctrl-Z。

POSIX字符类:

[:alnum:],等价于[A-Za-z0-9]

[:alpha:],等价于[A-Za-z]

[:blank:],等价于[\t ],注意这里有个空格

[:cntrl:],ASCII 0-31加上127

[:digit:],等价于[0-9]

[:graph:],和[:print:]一样,但是不包括空格

[:lower:],等价于[a-z]

[:print:],任何一个可打印字符

[:punct:],不属于[:alnum:]也不属于[:cntrl:]的字符

[:space:],等价于[\f\n\r\t\v ],注意有个空格

[:upper:],等价于[A-Z]

[:xdigit:],任何一个十六进制数字,等价于[a-FA-F0-9]

注意,实际使用时是再加一层[],也就是[[:xdigit:]],这么使用的。

 

第五章 重复匹配

@字符是不需要转义的,在正则中。

+匹配一个或多个字符(至少一个,不匹配0个字符),比如[0-9]+,将匹配一个或者多个数字。注意,如果是[0-9+]匹配的是数字0-9和加号。

匹配Email:

[\w.]+@[\w.]+\.\w+

注意,在字符集合[]里,.和+这样的元字符不需要转义。但是也可以转义,比如:[\w.]和[\w\.]是一样的。

*元字符匹配零个或者多个字符。

比如:

\w+[\w.]*@[\w.]+\.\w+

如果要匹配*本身,需要\*。

?是匹配一个或者零个字符。比如对Windows和Linux的文本结束进行匹配:

[\r]?\n[\r]?\n

如果需要输出?,需要\?。

{和}也是元字符,如果需要匹配本身,需要加\。

[[:xdigit:]]{6},表示匹配6次十六进制数。

{1,2},表示匹配1-2次,是一个区间。

{0,3},是匹配0-3次,注意重复次数可以是0次。

{3,},表示至少匹配3次。

.*表示一网打尽,全部匹配。

*和+是“贪婪型”元字符,它们的匹配模式是多多益善而不是适可而止。

*?是一种*的懒惰型版本,就是匹配成了就结束了,比如:

<B>AK</B> and <B>HI</B>

<[Bb]>.*?</[Bb]>

 

第六章 位置匹配

\bcat\b表示严格匹配cat。如果\bcat就是以cat开头的字符串。如果是cat\b,就是以cat结尾的字符串。类似的有\<\>写法,这种写法egrep支持。

\B-\B匹配一个前后都不是单词边界的连字符,比如 color - coded,这种就被匹配了。

如果某个文本,需要匹配它的第一行(注意第一行以外匹配不行,文本中没有空行),原文如下:

<?xml version="1.0" encoding="UTF-8" ?>

匹配如下:

^\s*<\?xml.*\?>

^\s*,以某些类型的空字符串开头,并且匹配多次,接着是<\?xml.*\?>这里的匹配,也就是文本开头可以是空行,但是不能有别的文字,也就是第一行有意义的文字就是<?xml version="1.0" encoding="UTF-8" ?>。这里的用法就是^,以什么开头,注意区别[^x],这是排除的用法。

$是以什么结尾的匹配,cat$,以cat结尾进行匹配。又如:\s*$匹配一个字符串结尾处的零个或者多个空白字符。

^.*$是正确的,它能匹配任何一个字符串,但是没什么用处。

分行匹配模式(multiline mode)使用(?m)记号,能改变其它元字符行为的元字符序列。在使用时,(?m)必须出现在整个模式的最前面。

比如文本是:

  // Make…

  // Init

  // Done

现在我们要找出其中的所有注释,也就是//这三行内容,匹配如下:

(?m)^\s*//.*$,这里(?m)会把换行符视为一个字符分割串,这样可以分别找出//这三行,如果不加(?m),则从第一行//到最后都会被匹配。并非所有正则都支持(?m)方法。

 

第七章 使用子表达式(subexpression)

https?://匹配http://或者https://。

&nbsp表示非换行型空格符。nbsp是non-breaking space缩写。

子表达式是一个更大的表达式的一部分,把一个表达式划分为一个子表达式的目的是为了把那些子表达式当做一个独立的元素来使用。子表达式使用(和)括起来。如果需要表达(和)本身,需要\。

比如:

(&nbsp;){2,}

针对&nbsp;这个表达式,进行至少2次匹配。

IP地址匹配:

\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3} ---> 没有考虑到超过255

也可以写成:

(\d{1,3}.){3}\d{1,3}

也可以写成:

(\d{1,3}.){3}(\d{1,3})

但是这么写,可能会影响速度。

又如:

(19|20)\d{2}

比如考虑一个完整的IP地址:

1、任何一个1位或者2位数字。

2、任何一个以1开头的三位数字。

3、任何一个以2开头的、第二位数字在0~4之间的三位数字。

4、任何一个以25开头的数字、第三位数字在0~5之间的三位数字。

((([1-9]{1,2}|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}(([1-9]{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))

 

第八章 回溯引用(backreference):前后一致匹配

.*是一种贪婪型匹配,.*?属于懒惰型匹配,.*会匹配尽可能的多,而.*?匹配到了就结束了。

回溯引用允许正则表达式模式引用前面的匹配结果。

看一个正则:

[ ]+(\w+)[ ]+\1

先匹配一个或者多个空格,然后匹配字符或者数字一个或者多个(注意子表达式),然后匹配一个或者多个空格,\1表示对(\w+)进行回溯引用。如果当(\w+)匹配的是某个字符串,比如and,则\1也匹配and。

比如:

And And

这种相同的文本,使用回溯引用非常有效果,可以反复引用已经有的匹配。

再看一个:

<[hH]([1-6])>.*?</[Hh]\1>

如果之前([1-6])匹配了,假如说是6,则\1也匹配6,是一个再次引用的过程。

如果\1表示第一个子表达式,那么\2表示第二个,\3表示第三个,以此类推。

也可以把回溯变量想象成变量。

当然不同语言对于正则表达式的回溯引用是不一样的。

(\0)可以用来代表整个正则表达式。

回溯引用不但可以用作搜索,也可以用作替换:

加入有段文件:

Hello, ben@forta.com is my email address.

< A HERF="mailto:$1">$1</A>

使用正则:

(\w+[\w\.]*@[\w\.]+\.\w+)

这里面会匹配到:

ben@forta.com

这里的子表达式中匹配的内容可以替换$1的变量。

又如:

文本:

313-555-1234

正则:

(\d{3})(-)(\d{3})(-)(\d{4})

替换:

($1) $3-$5

结果

(313) 555-1234

进行大小写转换的元字符:

\L$2\E,把第二个子表达式全部转换为小写

\U$2\E,把第二个子表达式全部转换为大写

\l,把写一个字符(或子表达式)转换为小写

\u,把下一个字符(或子表达式)转换为大写

 

第九章 前后查找

前后查找技术就是包含匹配本身但是不返回,只适用于确定正确的匹配位置,它并不是匹配结果的一部分。

常见的正则支持向前查找(lookahead),而向后查找(lookbehind)支持的不多。

向前查找指定了一个必须匹配但不在结果中返回的模式,实际上就是一个子表达式。实际上就是一个以?=开头的子表达式。

有些正则表达式文档使用术语"消费"来表述"匹配和返回文本"的含义。在向前查找中,被匹配的文本不包含在最终返回的匹配结果中,这被称为“不消费”。

比如:

文本:

http://www.forta.com/

https://mail.forta.com/

ftp://ftp.frota.com/

正则:

.+(?=:)

这里的(?=:)表示向:前查找,也就是匹配了:

http

https

ftp

注意,这里使用了子表达式。这里向前查找也就是定位而已并不会返回值。

又如正则:

.+(:)

这样匹配结果是:

https:

http:

ftp:

因为这里最终是被消费了,所以会输出:。

前后查找实际上是输出字节为0,也就是零宽度(zero-width)匹配操作。

向后查找类似向前查找,使用?<=符号。

向前向后查找都是用于一个子表达式中,而且后面要跟匹配的文本。

比如:

文本:

ABC01: $23.45

要取23.45

正则:

(?<=\$)[0-9.]+

匹配结果:

23.45

向前向后一起使用:

文本:

<TITLE>ben forta</TITLE>

正则:

(?<=<[tT][Ii][tT][Ll][Ee]>).*(?=<[Tt][iI][Tt][Ll][Ee]>)

对前后查找取非:

(?!),向前查找取非

(?<!),向后查找取非

比如:

文本:

I paid $30 for 100 apples.

正则:

\b(?<!\$)\d+\b

匹配结果:

100

如果不加\b,那么30中的0也会被匹配。

 

第十章 嵌入条件

并非所有正则表达式都支持条件处理。

正则表达式使用?来定义条件。格式如下:

(?(backreference)true-regex)

?表示是一个条件,backreference是一个回溯引用,如果回溯引用存在,则执行true-regex。

举个例子:

(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^]+>(?(1)\s*</<Aa>)

(<[Aa]\s+[^>]+>\s*)?这句表示有没有标签<A …>,<[Ii][Mm][Gg]\s+[^]+>这句表示一定有标签Img(不区分大小写),最后一句判断有标签<A…>再打印标签</Aa>。?(1)写法正确,?(\1)写法不对,但是也能用。

条件表达式的扩展如下:

(?(backreference)true-regex|false-regex)

如果回溯引用不存在,则执行false-regex。

又如:

(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}

(\()?先对(匹配一个或者0个,如果如果匹配了(,也匹配),如果不匹配,则后面的匹配-。

前后查找条件技术:

\d{5}(?(?=-)-\d{4})

先打印5个数字,然后5个数字后如果是一个-(向前匹配),再继续匹配-和四个数字。这里的向前匹配是不消费的,因此如果是类似于33333-这样的数据是不会被匹配的,而33333-3333是会被匹配的。

 

附录A:常见应用软件和编程语言中的正则表达式:

Grep:

-E,扩展正则

-G,基本正则

-P,使用Perl正则

-o,只查看匹配结果

-v,求非

-c,只要匹配行数不要细节

MySQL:

对正则表达式允许在WHERE子句中使用如下格式:

REGEXP "expression"

SELECT * FROM table WHERE REGEXP "pattern"

只提供搜索,不提供替换。

默认情况下,正则不区分大小写,如果想区分,要加binary关键字,放在REGEXP和模式之间。

用[[:<:]]匹配单词开头,[[:>:]]匹配单词结束。

不支持向前预测。

不支持嵌入操作。

不支持八进制字符搜索。

不支持\a,\b,\e,\f,\v。

不支持回溯引用。

 

附录B:常见问题的正则表达式解决方案

文本:

J. Doe: 248-555-1234

B. Smith: (313) 555-1234

A.lee: (810)555-1234

M. Jones: 734.555.999

要取:

248-555-1234

(313) 555-1234

(810)555-1234

正则:

[\(.]?[2=9]\d\d[\).]?[ -]?[2-9]\d\d[-.]\d{4}

URL匹配:

https?://(\w*:\w@)?[-\w.]+(:\d+)?(/([\w/_.]*(\?\S+)?)?)?

电子邮件匹配:

(\w+\.)*\w+@(\w+\.)+[A-Za-z]+

 

匹配索引:

基本元字符:

.,匹配任意单个字符。

|,逻辑或操作符。

[],匹配字符集合中的一个字符。

[^],对字符集求非。

-,定义一个区间,例如[A-Z]。

\,对下一个字符进行转义。

数量元字符:

*,匹配前一个字符(子表达式)的零次或者多次重复。

*?,*的懒惰型版本。

+,匹配前一个字符(子表达式)的一次或者多次重复。

+?,+的懒惰型版本。

?,匹配前一个字符(子表达式)的零次或者一次重复。

{n},匹配前一个字符(子表达式)的n次重复。

{m,n},匹配前一个字符(子表达式)至少m次至多n次的重复。

{n,},匹配前一个字符(子表达式)至少n次重复。

{n,}?,{n,}的懒惰型版本。

位置元字符:

^,匹配字符串的开头。

\A,匹配字符串的开头。

$,匹配字符串的结尾。

\Z,匹配字符串的结尾。

\<,匹配单词的开头。

\>,匹配单词的结束。

\b,匹配单词边界(开头和结束)。

\B,\b的反义。

特殊字符元字符:

[\b],退格字符。

\c,匹配一个控制字符。

\d,匹配任意数字字符。

\D,\d的反义。

\f,换页符。

\n,换行符。

\r,回车符。,

\s,匹配一个空白字符。

\S,\s的反义。

\t,制表符(Tab字符)。

\v,垂直制表符。

\w,匹配任意字母数字字符或者下划线字符。

\W,\w的反义。

\x,匹配一个十六进制数字。

\0,匹配一个八进制数字。

回溯引用和前后查找:

(),定义一个子表达式。

\1,匹配第一个子表达式,\2匹配第二个子表达式,以此类推。

?=,向前查找。

?<=,向后查找。

?!,向前查找取非。

?<!,向后查找取非。

?(),条件if then。

?()|,条件if then else。

大小写转换:

\E,结束\L或\U转换。

\l,把下一个字符或表达式转换为小写。

\L,把后面的字符或表达式转换为小写,直到\E。

\u,把下一个字符或表达式转换为大写。

\U,吧下一个字符或表达式转换为大写,直到\E。

匹配模式:

(?m),分行匹配模式。